Oracle 12c:Parallel SELECT FOR UPDATE NOWAIT on disjoint of rows causes ORA-00054: resource busy
Oracle 12c: Parallel SELECT FOR UPDATE NOWAIT on disjoint sets of rows causes ORA-00054: resource busy
我运行在几个并发进程中跟踪查询:
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;
- Table答:A_ID(PK)
- Table B: B_ID (PK), A_ID (FK)
- Table C: C_ID (PK), C_STATUS, B_ID (FK), D_ID (FK)
- Table D:D_ID (PK)
每个进程在 IN (...)
列表中都有自己的一组值,这些值保证是不相交的。
不确定这是否重要,但是到 table D 的 FK 也是不相交的——在更新之前和之后都是稍后完成的。
不过我偶尔会收到 ORA-00054: resource busy
,我读为 "two processes tried to lock the same row for update in a NOWAIT statement"。
在我使用 FOR UPDATE OF C_STATUS, D_ID NOWAIT
子句之前,并行查询正在等待其他查询完成(等待释放锁),而且在尝试更新 [= 的相应行时我很少遇到死锁70=] C:
死锁图:
---------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
锁定的行来自 table C。但是,在检查阻塞行上的调试日志和 rowid 时,我发现至少有一个进程不应该接触该行。
知道为什么我在通过多个进程更新不相交的行时会出现资源繁忙/死锁吗?为什么 Oracle 锁定实际未使用的行?
编辑:我能够将问题缩小到这个 bash 脚本:
#!/bin/bash
sub(){
sqlplus -S "$DB_ACCESS" << EOF
exec dbms_lock.sleep();
select ':'||C_ID from C where C_ID in ()
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 是不同的,我检查了 parent B_ID 和 grandparent 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 行加载到 table C 后重现此问题。
好的,我找到了根本原因。这是一个 ITL 锁定和等待。
更多信息在这里:ITL waits demystified
诀窍是这三行都是同一个物理块的一部分,该物理块几乎已满,并且这些行的锁也存储在物理块中。第三个锁没有足够的 space,因此第三个事务将等待。
我运行在几个并发进程中跟踪查询:
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;
- Table答:A_ID(PK)
- Table B: B_ID (PK), A_ID (FK)
- Table C: C_ID (PK), C_STATUS, B_ID (FK), D_ID (FK)
- Table D:D_ID (PK)
每个进程在 IN (...)
列表中都有自己的一组值,这些值保证是不相交的。
不确定这是否重要,但是到 table D 的 FK 也是不相交的——在更新之前和之后都是稍后完成的。
不过我偶尔会收到 ORA-00054: resource busy
,我读为 "two processes tried to lock the same row for update in a NOWAIT statement"。
在我使用 FOR UPDATE OF C_STATUS, D_ID NOWAIT
子句之前,并行查询正在等待其他查询完成(等待释放锁),而且在尝试更新 [= 的相应行时我很少遇到死锁70=] C:
死锁图:
---------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
锁定的行来自 table C。但是,在检查阻塞行上的调试日志和 rowid 时,我发现至少有一个进程不应该接触该行。
知道为什么我在通过多个进程更新不相交的行时会出现资源繁忙/死锁吗?为什么 Oracle 锁定实际未使用的行?
编辑:我能够将问题缩小到这个 bash 脚本:
#!/bin/bash
sub(){
sqlplus -S "$DB_ACCESS" << EOF
exec dbms_lock.sleep();
select ':'||C_ID from C where C_ID in ()
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 是不同的,我检查了 parent B_ID 和 grandparent 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 行加载到 table C 后重现此问题。
好的,我找到了根本原因。这是一个 ITL 锁定和等待。
更多信息在这里:ITL waits demystified
诀窍是这三行都是同一个物理块的一部分,该物理块几乎已满,并且这些行的锁也存储在物理块中。第三个锁没有足够的 space,因此第三个事务将等待。