oracle死锁在一个update语句中?

oracle死锁在一个update语句中?,oracle,sql-update,deadlock,Oracle,Sql Update,Deadlock,首先,我的oracle版本: SQL> select * from v$version; BANNER -------------------------------------------------------------------------------- Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production PL/SQL Release 11.2.0.4.0 - Producti

首先,我的oracle版本:

SQL> select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
CORE    11.2.0.4.0      Production
TNS for Linux: Version 11.2.0.4.0 - Production
NLSRTL Version 11.2.0.4.0 - Production
我创建表并插入两行:

create table test_table
(
    objectId VARCHAR2(40) not null,
    dependId VARCHAR2(40) not null
);

insert into test_table values(1, 10000);
insert into test_table values(2, 20000);
commit;
然后打开两个会话并按顺序执行以下命令

案例1:

第1次会议:

update test_table set dependId=100000 where objectid in (2);
update test_table set dependId=100000 where objectid in (1);
第2次会议:

update test_table set dependId=200000 where objectid in (1,2);
update test_table set dependId=200000 where objectid in (2,1);
见第1条:

update test_table set dependId=100000 where objectid in (1);
update test_table set dependId=100000 where objectid in (2);
会话2显示了ORA-00060:在等待资源时检测到死锁

案例2

第1次会议:

update test_table set dependId=100000 where objectid in (2);
update test_table set dependId=100000 where objectid in (1);
第2次会议:

update test_table set dependId=200000 where objectid in (1,2);
update test_table set dependId=200000 where objectid in (2,1);
见第1条:

update test_table set dependId=100000 where objectid in (1);
update test_table set dependId=100000 where objectid in (2);
不会出现僵局


请解释原因。
如何更新。。。在(1,2)中objectid的位置
保持锁?

这取决于数据库尝试获取行锁的顺序

在您的示例中,objectid=1是表中的“第一个”。您可以通过按rowid对数据进行排序来验证这一点:

create table test_table
(
    objectId VARCHAR2(40) not null,
    dependId VARCHAR2(40) not null
);

insert into test_table values(1, 99);
insert into test_table values(2, 0);
commit;

select rowid, t.* from test_table t
order  by rowid;

ROWID                 OBJECTID    DEPENDID   
AAAT9kAAMAAAdMVAAA    1           99          
AAAT9kAAMAAAdMVAAB    2           0     
如果在会话1中,您现在运行:

update test_table set dependId=100000 where objectid in (2);
update test_table set dependId=100000 where objectid in (1);
您正在更新表中的“第二”行。当会话2运行时:

update test_table set dependId=200000 where objectid in (2,1);
它读取数据块。然后尝试按存储顺序获取锁。因此,它查看第一行(objectid=1),询问“是否已锁定?”发现答案为否,并锁定该行

然后对第二行重复此过程。被会话1锁定的。当查询
v$lock
时,您应该看到两个条目请求lmode=6中的“TX”锁。每届会议一名:

select sid from v$lock
where  type = 'TX'
and    lmode = 6;

SID   
    75 
    60 
所以在这个阶段,两个会话都锁定了一行。会话2正在等待会话1

在会话1中,您现在运行:

update test_table set dependId=100000 where objectid in (2);
update test_table set dependId=100000 where objectid in (1);
嘘!死锁

好的,但是我们如何确定这是由于存储了订单行

使用属性聚类(12c特性),我们可以更改行存储在块中的顺序,因此objectid=2为“第一”:

重复测试。在第1次会议上:

update test_table set dependId=100000 where objectid in (2);
因此,这已锁定“第一”行。第2次会议:

update test_table set dependId=200000 where objectid in (2,1);
这将尝试锁定“第一”行。但不能,因为会话1已锁定。因此,此时只有会话1持有任何锁

检查
v$lock
以确保:

select sid from v$lock
where  type = 'TX'
and    lmode = 6;

SID   
    60 
当然,当您在会话1中运行第二次更新时,它将完成:

update test_table set dependId=100000 where objectid in (1);

注意

这并不意味着
update
可以保证按存储在表块中的顺序锁定行。添加或删除索引可能会影响此行为。Oracle数据库版本之间的更改也是如此

关键的一点是,
update
必须以某种顺序锁定行。它无法立即获取它将更改的所有行上的锁


因此,如果您有两个或多个具有多个更新的会话,则可能会出现死锁。因此,您应该通过使用
select锁定所有要更改的行来启动事务。。。对于更新

您是说在案例2中,会话2中发布的更新成功地更新了表吗?我不这么认为。也许您没有收到ORA-00060消息(即会话一直在等待),但我很确定您没有更新2行。@Littlefoot-不,滴答声非常正确:案例2更新了
2行。
在会话中#2 when session#1提交。当然,@APC-when session#1提交。但是,在它完成之前,第2课时会等待。绝对令人困惑。我已经在Oracle 12c数据库上使用单独的SQL*Plus会话验证了这种行为。我在表中又插入了几行,似乎ORA-00060只有在按照会话1更新高编号、会话2更新低编号、高编号、会话1更新低编号的顺序完成时才会发生。否则,会话2将一直挂起,直到会话1提交为止。@Littlefoot在案例2中会话2等待,但不会死锁,而在案例1中会话2将死锁,我想知道原因。