我正在以下幾個併發進程查詢:甲骨文12C:並行選擇一種獨立集合行的UPDATE NOWAIT導致ORA-00054:資源忙
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的各行中得到死鎖:
死鎖圖:阻斷行我發現,過程中的至少一個是不該碰的行檢查調試日誌與rowid然而,當
---------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.。
任何想法,當我通過多個進程更新不相交的行時,爲什麼會導致資源忙/死鎖?爲什麼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_IDs是不同的,我檢查了父母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.
我希望我能提供的樣本數據,但我只能重現此問題的加載〜百萬行插入表格後C.
是否所有的'* _ID'列都被索引? –
是的,所有的PK和FK都被編入索引。 – Blaf
如果我讀了這個權利,「在我有FOR UPDATE OF C_STATUS,D_ID NOWAIT子句之前,並行查詢正在等待其他人完成」是不正確的。在添加FOR UPDATE之前,查詢可以併發運行,因爲Oracle不會在讀取時鎖定記錄。但是,現在,使用此子句,您將在每次讀取時鎖定行,因此,第一個會話將獲得鎖,第二個會話(如果在第一個會話仍在運行時運行)將收到有關已鎖定行的錯誤。 – unleashed