和外來鍵相關的阻塞和死鎖問題總結

warehouse發表於2010-10-13

外來鍵上沒有index操作主表資料時常會引起阻塞和deadlock問題

[@more@]

外來鍵引起的阻塞:
session 1:
SQL> create table p(id int primary key ) tablespace users;

表已建立。

SQL> create table r(id int references p on delete cascade) tablespace users;

表已建立。

SQL> insert into p values(1);

已建立 1 行。

SQL> insert into p values(2);

已建立 1 行。

SQL> insert into p values(3);

已建立 1 行。

SQL> commit;

提交完成。

SQL> select distinct sid from v$mystat;

SID
----------
159

SQL> select * from p;

ID
----------
1
2
3
SQL> delete from p where id=1;

已刪除 1 行。

SQL>
--=======================
session 3:
SQL> select sid,type,id1,id2,lmode,request,ctime,block from v$lock where sid in
2 (159,128) order by sid;

SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK
---------- -- ---------- ---------- ---------- ---------- ---------- ----------
159 TM 13017 0 3 0 9 0
159 TX 589854 449 6 0 9 0
159 TM 13020 0 3 0 9 0

SQL> col object_name format a10
SQL> select object_id,object_name from dba_objects where object_id in (13017,130
20);

OBJECT_ID OBJECT_NAM
---------- ----------
13017 P
13020 R
--=========================
很明顯session 1中的語句delete from p where id=1;在字表r上加了鎖,mode為3;
--=========================
session 2:
SQL> select distinct sid from v$mystat;

SID
----------
128

SQL> delete from p where id=2;
等待、被session 1阻塞了
--=========================
session 3:
SQL> select sid,type,id1,id2,lmode,request,ctime,block from v$lock where sid in
2 (159,128) order by sid;

SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK
---------- -- ---------- ---------- ---------- ---------- ---------- ----------
128 TM 13017 0 3 0 9 0
128 TM 13020 0 0 5 9 0
159 TX 589854 449 6 0 90 0
159 TM 13020 0 3 0 90 1
159 TM 13017 0 3 0 90 0

SQL>
很明顯session 2中的語句delete from p where id=2;請求鎖mode為5被
session 1中的語句delete from p where id=1加在r上的鎖mode為3阻塞了;
由於mode 3對應的RX(row exclusive)和mode 5對應的SRX(share row exclusive)不能相容,因此
session 2被阻塞了。
--=========================
外來鍵引起的死鎖:
rollback掉上面session 1和session 2中的sql
--=========================
session 1:
SQL> insert into r values(2);

已建立 1 行。

SQL>
--=======================
session 3:
SQL> select sid,type,id1,id2,lmode,request,ctime,block from v$lock where sid in
2 (159,128) order by sid;

SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK
---------- -- ---------- ---------- ---------- ---------- ---------- ----------
159 TM 13017 0 2 0 15 0
159 TX 393228 371 6 0 15 0
159 TM 13020 0 3 0 15 0

SQL>
--=======================
session 2:
SQL> insert into r values(2);

已建立 1 行。

SQL>
--=======================
session 3:
SQL> select sid,type,id1,id2,lmode,request,ctime,block from v$lock where sid in
2 (159,128) order by sid;

SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK
---------- -- ---------- ---------- ---------- ---------- ---------- ----------
128 TM 13017 0 2 0 14 0
128 TX 262168 360 6 0 14 0
128 TM 13020 0 3 0 14 0
159 TX 393228 371 6 0 55 0
159 TM 13020 0 3 0 55 0
159 TM 13017 0 2 0 55 0

已選擇6行。

SQL>
--=======================
當我們向子表r中分別透過session 1和2插入資料時,此時發現在r表上家了鎖mode是3,在主表
p上加的鎖mode是2(RS: row share),此時別沒有阻塞,接下來操作主表...
session 1:
SQL> delete from p where id=1;
阻塞...
session 3:
SQL> select sid,type,id1,id2,lmode,request,ctime,block from v$lock where sid in
2 (159,128) order by sid;

SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK
---------- -- ---------- ---------- ---------- ---------- ---------- ----------
128 TM 13017 0 2 0 332 0
128 TX 262168 360 6 0 332 0
128 TM 13020 0 3 0 332 1
159 TX 393228 371 6 0 373 0
159 TM 13020 0 3 5 373 0
159 TM 13017 0 3 0 29 0

已選擇6行。

SQL>
從最後一個欄位block=1發現session 1(sid:159)請求的鎖mode 5被session 2(sid:128)加在子表(r)
上的3鎖阻塞...(上面已經說了mode 3和5不能相容);
--=========================
session 2:
SQL> delete from p where id=3;
阻塞...
session 3:
SQL> select sid,type,id1,id2,lmode,request,ctime,block from v$lock where sid in
2 (159,128) order by sid;

SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK
---------- -- ---------- ---------- ---------- ---------- ---------- ----------
128 TM 13017 0 3 0 30 0
128 TX 262168 360 6 0 569 0
128 TM 13020 0 3 5 569 0
159 TX 393228 371 6 0 610 0
159 TM 13020 0 3 0 610 1
159 TM 13017 0 2 0 30 0

已選擇6行。

SQL>
從最後一個欄位block=1發現session 2(sid:128)請求的鎖mode 5被session 1(sid:159)加在子表(r)
上的3鎖阻塞...(上面已經說了mode 3和5不能相容);這樣session 1和session 2相互阻塞最終形成了死鎖
,當然死鎖oracle會自動偵測並且解除,於是session 1中出現了deadlock被解除的提示資訊:
session 1:
SQL> delete from p where id=1;
delete from p where id=1
*
第 1 行出現錯誤:
ORA-00060: 等待資源時檢測到死鎖


SQL>
--=========================
如何避免使用外來鍵而引起的死鎖:
rollback掉上面session 1和session 2中的sql:
session 1:
SQL> select * from p;

ID
----------
1
2
3

SQL> select * from r;

未選定行

SQL> create index idx_r on r(id) tablespace users;

索引已建立。

SQL> insert into r values(2);

已建立 1 行。

SQL>
--========================
session 3:
SQL> select sid,type,id1,id2,lmode,request,ctime,block from v$lock where sid in
2 (159,128) order by sid;

SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK
---------- -- ---------- ---------- ---------- ---------- ---------- ----------
159 TM 13017 0 2 0 78 0
159 TX 131078 387 6 0 78 0
159 TM 13020 0 3 0 78 0

SQL>
--=======================
session 2:
SQL> insert into r values(2);

已建立 1 行。

SQL>
--========================
session 3:
SQL> select sid,type,id1,id2,lmode,request,ctime,block from v$lock where sid in
2 (159,128) order by sid;

SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK
---------- -- ---------- ---------- ---------- ---------- ---------- ----------
128 TM 13017 0 2 0 22 0
128 TX 393219 372 6 0 22 0
128 TM 13020 0 3 0 22 0
159 TX 131078 387 6 0 124 0
159 TM 13020 0 3 0 124 0
159 TM 13017 0 2 0 124 0

已選擇6行。

SQL>
--==========================
session 1:
SQL> delete from p where id=1;

已刪除 1 行。

SQL>
--========================
session 3:
SQL> select sid,type,id1,id2,lmode,request,ctime,block from v$lock where sid in
2 (159,128) order by sid;

SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK
---------- -- ---------- ---------- ---------- ---------- ---------- ----------
128 TM 13017 0 2 0 121 0
128 TX 393219 372 6 0 121 0
128 TM 13020 0 3 0 121 0
159 TX 131078 387 6 0 223 0
159 TM 13020 0 3 0 223 0
159 TM 13017 0 3 0 27 0

已選擇6行。

SQL>
--=======================
我們發現在子表r上建立了index之後,session 1(sid:159)中的操作delete from p where id=1執行之後
加在主表p上的鎖mode由原來的2變成了3,而且沒有index之前請求的子表r上的鎖mode是5,現在變成了3,
鎖mode 3和session 2裡面insert操作引起的鎖mode 3在行級鎖不發生衝突(因為2個session操作的r表裡的資料不是同一行)
的情況下是可以相容的,因此session 2(sid:128)不在阻塞session 1;
--====================
session 2:
SQL> delete from p where id=3;

已刪除 1 行。

SQL>
--====================
session 3:
SQL> select sid,type,id1,id2,lmode,request,ctime,block from v$lock where sid in
2 (159,128) order by sid;

SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK
---------- -- ---------- ---------- ---------- ---------- ---------- ----------
128 TM 13017 0 3 0 30 0
128 TX 393219 372 6 0 541 0
128 TM 13020 0 3 0 541 0
159 TX 131078 387 6 0 643 0
159 TM 13020 0 3 0 643 0
159 TM 13017 0 3 0 447 0

已選擇6行。

SQL>
同理r上有了index之後,session 2(sid:128)中的操作delete from p where id=3執行之後
加在主表p上的鎖mode由原來的2變成了3,而且沒有index之前請求的子表r上的鎖mode是5,現在變成了3,
鎖mode 3和session 1裡面insert操作引起的鎖mode 3在行級鎖不發生衝突(因為2個session操作的r表裡的資料不是同一行)
的情況下是可以相容的,因此session 1(sid:159)不再阻塞session 2;沒有了相互阻塞,死鎖也就不會發生了,
因此在外來鍵上建立index不僅會提高由於操作(比如delete主表資料)主表主鍵
資料而引起的掃描子表的效率而且也可以避免操作主表資料而引起的阻塞和死鎖問題

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/19602/viewspace-1039681/,如需轉載,請註明出處,否則將追究法律責任。

相關文章