更新时Oracle死锁

时间:2015-05-11 10:29:27

标签: oracle sql-update database-deadlocks

我有一个数据库,有些人和服务同时用它来更新个人数据。

因此有可能一个人更新一行并忘记提交。稍后更新服务总是想要更新此行并且服务挂起,直到提交或会话将被关闭。这可能需要一些时间,而更新服务无法完成其工作。

我可以确定这种情况吗? 更新服务可能会发送如下语句:

UPDATE person 
  SET email_address = 'new.email@company.com' WHERE person_id='1234567'
ON LOCKED ERROR;

如果此行有锁定,则此语句应该出错。

或者我可以配置oracle服务器在定义的时间后发送错误代码,如果一行上的锁没有完成?

感谢您的帮助。

2 个答案:

答案 0 :(得分:3)

  

一个人更新一行并忘记提交。稍后更新服务总是想要更新此行并且服务挂起,直到提交或会话将被关闭

理想情况下,读者不会阻止作者,作者也不会阻止读者

您所描述的是 DEADLOCK 方案。当会话执行更新时,它会获取行级别独占锁,而其他会话尝试更新这些行,需要等到COMMIT / ROLLBACK释放锁。

当两个或多个会话相互等待锁定时发生死锁。

  

或者我可以配置oracle服务器在定义的时间后发送错误代码,如果一行上的锁没有完成?

要检查阻止会话和等待课程,您可以查询 v $ session 视图:

select sid,
       status,
       program,
       sql_id, 
       state, 
       wait_class, 
       blocking_session_status,
       event 
from v$session;

当遇到死锁时, Oracle自动检测到死锁,抛出ORA-00060:在等待资源时检测到死锁,并回滚Oracle确定为死锁的死锁中涉及的一个事务受害者。以前成功的事务不会回滚。即使在死锁错误之后,如果发出提交,也会提交先前成功的事务。此时,其他会话的事务也将成功,您可以发出提交。您无需在此明确执行任何操作。死锁会自动清除 - 您永远不需要清除它们。

查看我在此处回答的类似问题https://stackoverflow.com/a/28455397/3989608

有关死锁的详细演示和示例,请参阅Understanding Oracle Deadlock

如果您使用 FOR UPDATE NOWAIT ,则Oracle不会让您更新这些行并抛出以下错误:

ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired

例如,

第1节:

SQL> SELECT empno, deptno
  2    FROM emp  WHERE
  3   deptno = 10
  4  FOR UPDATE NOWAIT;

     EMPNO     DEPTNO
---------- ----------
      7782         10
      7839         10
      7934         10

SQL>

第2节:

SQL> SELECT empno, deptno
  2    FROM emp  WHERE
  3   deptno in (10, 20)
  4  FOR UPDATE NOWAIT;
  FROM emp  WHERE
       *
ERROR at line 2:
ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired

更进一步,如果您想避免更新已锁定的行,可以使用 FOR UPDATE SKIP LOCKED 子句来避免其他会话被提取要更新的行已经锁定。

例如,

第1节:

SQL> SELECT empno, deptno
  2    FROM emp  WHERE
  3   deptno = 10
  4  FOR UPDATE NOWAIT;

     EMPNO     DEPTNO
---------- ----------
      7782         10
      7839         10
      7934         10

SQL>

第2节:

SQL> SELECT empno, deptno
  2    FROM emp  WHERE
  3   deptno in (10, 20)
  4  FOR UPDATE NOWAIT;
  FROM emp  WHERE
       *
ERROR at line 2:
ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired

现在让我们跳过会话1锁定的行

SQL> SELECT empno, deptno
  2    FROM emp  WHERE
  3   deptno IN (10, 20)
  4  FOR UPDATE SKIP LOCKED;

     EMPNO     DEPTNO
---------- ----------
      7369         20
      7566         20
      7788         20
      7876         20
      7902         20

SQL>

因此,部门= 10被会话1锁定,然后部门= 20被会话2锁定。

请参阅有关避免已锁定行Oracle deadlock keeps repeating on the same record

上的更新的类似问题

答案 1 :(得分:2)

你可以分两个阶段完成;使用circle.setPosition(ol.proj.transform([GPS_lon, GPS_lat], 'EPSG:4326', 'EPSG:3857')); 查询表,如果该行已被锁定则会抛出异常,如果没有错误则执行更新(和提交):

FOR UPDATE NOWAIT

如果你是从两个会话那样做,没有提交,那么运行select的第二个会看到:

  

ORA-00054:资源繁忙,并在指定NOWAIT或超时过期时获取

您还可以使用SELECT * FROM person WHERE person_id = 1234567 FOR UPDATE NOWAIT; UPDATE person SET email_address = 'new.email@company.com' WHERE person_id='1234567'; 超时:

WAIT <time>

在这种情况下,如果第一个没有在3秒内提交/回滚,第二个调用者将收到错误:

  

ORA-30006:资源繁忙;获取WAIT超时已到期

如果您通过JDBC或OCI等方式调用此方法,则可能需要在同一会话中进行单独的背对背呼叫,并且如果第一次呼叫处理异常,则需要抛出。