oracle为什么尽量不要使用外键的最好理解

对于死锁问题相信大家都是很头疼的,为什么不要使用外键呢?最简单的回答就是太容易产生死锁了。

经过个人的测试,我发现外键删除的时候,是按照表会话的顺序执行的,也就是说如果只有一个事务,只要子表删掉外键表的项,外键表就可以删除,但是如果同时有多个事务,这就难说了,具体举例子如下:

首先我们创建表并创建数据,顺序执行如下代码:

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

猜你喜欢

转载自www.cnblogs.com/ningxinjie/p/12765483.html