2017-03-16 49 views
0

我正在以下幾個併發進程查詢:甲骨文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.

+0

是否所有的'* _ID'列都被索引? –

+0

是的,所有的PK和FK都被編入索引。 – Blaf

+0

如果我讀了這個權利,「在我有FOR UPDATE OF C_STATUS,D_ID NOWAIT子句之前,並行查詢正在等待其他人完成」是不正確的。在添加FOR UPDATE之前,查詢可以併發運行,因爲Oracle不會在讀取時鎖定記錄。但是,現在,使用此子句,您將在每次讀取時鎖定行,因此,第一個會話將獲得鎖,第二個會話(如果在第一個會話仍在運行時運行)將收到有關已鎖定行的錯誤。 – unleashed

回答

0

OK,我找到了根本原因。這是一個ITL鎖&等待。

此處瞭解詳情:ITL waits demystified

訣竅是,三排是相同的物理塊中的一部分,其幾乎完全和這些行的鎖也被存儲在物理塊。第三把鎖沒有足夠的空間,因此第三筆交易將等待。

+0

你有多少個ITL? – BobC

+0

以前我沒有設置INITRANS,只存儲了兩個ITL。我把它增加到了32,這很好。 – Blaf