對於死鎖問題相信大家都是很頭疼的,為什么不要使用外鍵呢?最簡單的回答就是太容易產生死鎖了。
經過個人的測試,我發現外鍵刪除的時候,是按照表會話的順序執行的,也就是說如果只有一個事務,只要子表刪掉外鍵表的項,外鍵表就可以刪除,但是如果同時有多個事務,這就難說了,具體舉例子如下:
首先我們創建表並創建數據,順序執行如下代碼:
create table t_p (id number primary key, name varchar2(30)); create table t_f (fid number, f_name varchar2(30), foreign key (fid) references t_p); insert into t_p values (1, 'a'); insert into t_f values (1, 'a'); insert into t_p values (2, 'b'); insert into t_f values (2, 'c'); commit;
然后測試如下:我在本用戶下(C##BENDIHELI)和System下開啟兩個sql工作表:
其中在C##BENDIHELI記為sql1,system下記為sql2
如果程序的執行順序為:
1.sql1:delete t_f where fid = 2;
2.sql2:delete c##bendiheli.t_f where fid = 1;
3.sql1:delete t_p where id = 2;
此時sql1等待,因為sql2未提交(這就是我理解的外鍵表需要找會話,本會話可以繼續,如果有其他會話未提交,他就等待)
4.sql2:delete c##bendiheli.t_p where id = 1;
此時sql2等待,因為sql1未提交
現在的程序sql1的事務等待sql2的提交,sql2的事務等待sql1的提交,完了,鎖住了。
至於本人理解外鍵是按照會話尋找的原因是,即使我把2換成insert into c##bendiheli.t_f values(1,'duidu'),3仍然會等待,因為2沒提交
若我執行順序為如下,則可以,這樣就更能理解我的想法,就是外鍵按照非本會話的會話順序執行
1.sql1:delete t_f where fid = 2;
2.sql2:insert into c##bendiheli.t_f values(1,'duidu')
3.sql1:delete t_p where id = 2;
此時sql1等
4.sql2:commit;
提交后sql1就不再等待
解決方案:外鍵加索引
create index ind_t_f_fid on t_f(fid);
個人理解,這樣就解決了外鍵按照會話找連接表的,而是通過索引來找,這樣以來上方出現死鎖的程序就不再出現死鎖,因為這樣在本事務內,直接找索引即可。
本文的理解借鑒於博主:https://blog.csdn.net/fenyu8/article/details/53811686