Oracle 12c:并行SELECT FOR FOR UPDATE NOWAIT在不相交的行集上导致ORA-00054:资源忙

时间:2017-03-16 07:11:48

标签: sql oracle oracle12c database-deadlocks

我在几个并发进程中运行以下查询:

SELECT A_ID, B_ID, C_ID, C_STATUS, D_ID
FROM A NATURAL JOIN B NATURAL JOIN C 
WHERE A_ID IN (...)
FOR UPDATE OF C_STATUS, D_ID NOWAIT;
  • 表A:A_ID(PK)
  • 表B:B_ID(PK),A_ID(FK)
  • 表C:C_ID(PK),C_STATUS,B_ID(FK),D_ID(FK)
  • 表D:D_ID(PK)

每个进程在IN (...)列表中都有自己的一组值,这些集合保证不相交。

不确定是否重要,但表D的FK也是不相交的 - 无论是在更新之前还是之后都会完成。

然而,我偶尔会得到ORA-00054: resource busy,我将其读作“两个进程试图在NOWAIT语句中锁定同一行以进行更新”。

在我有FOR UPDATE OF C_STATUS, D_ID NOWAIT子句之前,并行查询正在等待其他人完成(等待释放锁),并且在尝试更新表C的各行时,我很少遇到死锁: / p>

死锁图:

                        ---------Blocker(s)--------  ---------Waiter(s)---------
Resource Name           process session holds waits  process session holds waits
TX-000F001F-0000F3B5-.. 39    1414     X             75     835           S
TX-0009000B-000124A5-.. 75     835     X             39    1414           S

锁定的行来自表C.但是当检查阻塞行上的调试日志和rowid时,我发现至少有一个进程不应该触及该行。

知道为什么在多个进程更新不相交的行时,我会遇到资源忙/死锁?为什么Oracle锁定实际上没有使用的行?

编辑:我能够将问题缩小到这个bash脚本:

#!/bin/bash
sub(){
sqlplus -S "$DB_ACCESS" << EOF
exec dbms_lock.sleep($2);
select '$1:'||C_ID from C where C_ID in ($3)
for update nowait;
exec dbms_lock.sleep(2);
rollback;
EOF
}
sub 1 0.1 1510223
sub 2 0.3 1510600
sub 3 0.5 1512100
wait

你可以看到C_ID是不同的,我检查了父B_ID和祖父母A_ID也是不同的。

我得到以下输出:

PL/SQL procedure successfully completed.
'1:'||C_ID
------------------------------------------
1:1510223
PL/SQL procedure successfully completed.
Rollback complete.
PL/SQL procedure successfully completed.
'2:'||C_ID
------------------------------------------
2:1510600
PL/SQL procedure successfully completed.
Rollback complete.
PL/SQL procedure successfully completed.
select '3:'||C_ID from C where C_ID in (1512100)
*
ERROR at line 1:
ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired
PL/SQL procedure successfully completed.
Rollback complete.

我希望我能提供样本数据,但我只能在将~1000000行加载到表C后才能重现此问题。

1 个答案:

答案 0 :(得分:0)

好的,我找到了根本原因。这是一个ITL锁&amp;等待。

此处有更多信息:ITL waits demystified

技巧是这三行都是同一物理块的一部分,几乎已满,这些行的锁也存储在物理块中。第三个锁没有足够的空间,因此第三个交易会等待。