现象:一个很简单的程序在压力测试过程中发现死锁,查看trace文件,发现如下信息:

Deadlock graph:
---------Blocker(s)-------- ---------Waiter(s)---------
Resource Name process session holds waits process session holds waits
TM-0000627d-00000000 21 1062 SX SSX 22 1092 SX SSX
TM-0000627d-00000000 22 1092 SX SSX 21 1062 SX SSX
session 1062: DID 0001-0015-0000001E session 1092: DID 0001-0016-0005C757
session 1092: DID 0001-0016-0005C757 session 1062: DID 0001-0015-0000001E
Rows waited on:
Session 1092: no row
Session 1062: no row
而引发这个死锁的sql如下:
Current SQL Statement:
DELETE FROM TABLE WHERE COL = :B1

[@more@]

首先注意到的是Deadlock graph中的资源占有情况,可以看到两个session都hold了一个SZ类型的锁,同时在等待SSX类型的锁,而且引发的是一个删除语句,并且这个表是系统的一个关键表,大部分的表的外键都引用自此表的主键。因此猜测是碰到了外键引发的死锁。试验如下:
1、创建一个表,此表作为子表
SQL> create table fk_table as select * from user_objects;

Table created
2、创建一个表,此表作为父表
SQL> create table pk_table as select * from user_objects;

Table created
3、创建父表的主键
SQL> alter table PK_TABLE add constraint pk_pktable primary key (OBJECT_ID);

Table altered
4、创建子表的外键
SQL> alter table FK_TABLE add constraint fk_fktable foreign key (OBJECT_ID) references pk_table (OBJECT_ID);

Table altered
5、如下sql取自TOAD工具,用来显示数据库锁的信息
SELECT LK.SID,
SE.USERNAME,
SE.OSUSER,
SE.MACHINE,
DECODE(LK.TYPE,
'TX',
'Transaction',
'TM',
'DML',
'UL',
'PL/SQL User Lock',
LK.TYPE) LOCK_TYPE,
DECODE(LK.LMODE,
0,
'None',
1,
'Null',
2,
'Row-S (SS)',
3,
'Row-X (SX)',
4,
'Share',
5,
'S/Row-X (SSX)',
6,
'Exclusive',
TO_CHAR(LK.LMODE)) MODE_HELD,
DECODE(LK.REQUEST,
0,
'None',
1,
'Null',
2,
'Row-S (SS)',
3,
'Row-X (SX)',
4,
'Share',
5,
'S/Row-X (SSX)',
6,
'Exclusive',
TO_CHAR(LK.REQUEST)) MODE_REQUESTED,
TO_CHAR(LK.ID1) LOCK_ID1,
TO_CHAR(LK.ID2) LOCK_ID2,
OB.OWNER,
OB.OBJECT_TYPE,
OB.OBJECT_NAME,
LK.BLOCK,
SE.LOCKWAIT
FROM V$LOCK LK, DBA_OBJECTS OB, V$SESSION SE
WHERE LK.TYPE IN ('TM', 'UL')
AND LK.SID = SE.SID
AND LK.ID1 = OB.OBJECT_ID(+);

6、执行一个删除操作,这时候在子表和父表上都加了一个Row-S(SX)锁
delete from fk_table where object_id=94716;
delete from pk_table where object_id=94716;
7、执行另一个删除操作,发现这时候第二个删除语句等待
delete from fk_table where object_id=94702;
delete from pk_table where object_id=94702;
执行查询语句,得到锁信息如下:
857 DML Row-S (SS) None 107220 0 BILL TABLE PK_TABLE 0 00000000D555A0E8
872 DML Row-X (SX) None 107220 0 BILL TABLE PK_TABLE 0
857 DML Row-X (SX) S/Row-X (SSX) 107219 0 BILL TABLE FK_TABLE 0 00000000D555A0E8
872 DML Row-X (SX) None 107219 0 BILL TABLE FK_TABLE 1
可以看到session 857在请求一个SSX类型的锁,此时如果执行如下操作:
delete from pk_table where object_id=94716;
死锁马上发生,因为857 SESSION拿到了一个对FK_TABLE的行独占锁,并在请求一个表共享锁,而872 SESSION也拿到了一个FK_TABLE上的行独占锁,并请求一个表共享锁。此时两个session谁都不会释放独占锁,并同时请求表的共享锁,死锁由此引发。因为死锁引发的时候两个session不是在等待对数据行进行加锁,所以可以从trace文件中发现等待的行都为no row,同时可以看到两个session都hold了一个SX锁,并且都在等待SSX锁资源。同时trace文件中还记录了引发死锁的sql。

Deadlock graph:
---------Blocker(s)-------- ---------Waiter(s)---------
Resource Name process session holds waits process session holds waits
TM-0001a2d3-00000000 16 872 SX SSX 20 857 SX SSX
TM-0001a2d3-00000000 20 857 SX SSX 16 872 SX SSX
session 872: DID 0001-0010-000F5EA0 session 857: DID 0001-0014-001D7407
session 857: DID 0001-0014-001D7407 session 872: DID 0001-0010-000F5EA0
Rows waited on:
Session 857: no row
Session 872: no row

Current SQL Statement:
delete from pk_table where object_id=94716
8、当对子表的外键列添加索引后,死锁被消除,因为这时删除父表记录不需要对子表加表级锁,这里不再做测试。

结论:曾经有人讨论过是否所有的数据库设计都应该遵守范式的规范,都把主外键关系建立起来。也有人反对这样做,因为这样复杂的关系在OLTP系统中可能会成为灾难,而提倡通过程序来保证数据的完整性,但程序发生bug导致数据不一致的情况时有发生。而且如果外键设置为级联删除,则不加索引的外键会使得对子表的记录删除走全表扫描。因此,对外键的使用还是要慎重!

btw:为什么删除子表记录的时候要加表级的共享锁呢?还有这个加锁好像只是一瞬间的,期待深入!!!

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/25016/viewspace-923330/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/25016/viewspace-923330/

外键字段未建索引引发的死锁相关推荐

  1. [数据库03]-约束(唯一性-主键-外键/存储引擎/事务/索引/视图/DBA命令/数据库设计三范式

    [数据库03]-约束(唯一性-主键-外键)/存储引擎/事务/索引/视图/DBA命令/数据库设计三范式 一.约束 1.1 唯一性约束(unique) 1.2 主键约束 1.3 外键约束 二.存储引擎 2 ...

  2. 三大范式,ER图,外键,视图,索引,触发器

    目录 一.数据库三大范式 二.ER图和实例 2.1.什么是ER图: 2.2.ER图核心要素: 2.3.画ER图和数据建模 三.外键约束与级联操作 3.1.什么是外键? 3.2.什么是外键约束? 3.3 ...

  3. sqlserver怎么查看索引_Sql Server之旅——第六站 为什么都说状态少的字段不能建索引...

    我们在学sqlserver的时候,大多教科书和前辈们都说状态少的字段不要建索引,由此带来的开销还不如不建索引,但是这句话有多少人真的知道,或者说有多少人真的对此有比较深刻的理解,而不是听别人道听途说. ...

  4. 为什么重复值高的字段不能建索引(比如性别字段等)

    结论(以innodb为例) a.非聚簇索引存储了对主键的引用,如果select字段不在非聚簇索引内,就需要跳到主键索引(上图中从右边的索引树跳到左边的索引树),再获取select字段值 b.如果非聚簇 ...

  5. Sql Server之旅——第六站 为什么都说状态少的字段不能建索引

    我们在学sqlserver的时候,大多教科书和前辈们都说状态少的字段不要建索引,由此带来的开销还不如不建索引,但是这句话有多少人真的知道,或者说有多少人真的对此有比较深刻的理解,而不是听别人道听途说. ...

  6. oracle外键有什么用,深入理解Oracle索引(20):外键是否应该加索引

    先表明我的立场.我是绝对支持外键一定要加索引! 虽然在高版本的Oracle里.对这个要求有所降低.但依然有如下原因: ① 死锁 外键未加索引是导致死锁的最主要原因.因为 无论更新父表主键.或者删除一个 ...

  7. MySQL数据库基础(外键约束、添加索引)

    文章目录 一.外键约束 1.外键概念 2.关联约束 3.添加与删除外键 4.集联删除 二.MySQL索引 1.创建唯一索引(三种方法) 2.索引查询 3.全文索引 4.联合索引 5.删除索引 一.外键 ...

  8. mysql 错误号1553 MySQL Cannot drop index needed in a foreign key constraint,外键也会创建索引

    表 被参照表: | teacher | CREATE TABLE `teacher` (`id` varchar(20) NOT NULL,`name` varchar(255) DEFAULT NU ...

  9. day08 外键字段的增删改查 正向反向插叙概念 跨表查询 聚合查询与分组查询 F查询

    day08 外键字段的增删改查 正向反向插叙概念 跨表查询 聚合查询与分组查询 F查询 昨日内容复习 自定义过滤器.标签.inclusion_tag 1.首先现在应用目录下创建名字为templatet ...

最新文章

  1. 《C#精彩实例教程》小组阅读10 -- C#属性与方法
  2. WebPack在多页应用项目中的探索
  3. 《重新认识你自己》八:与真实的自我相处
  4. Coding:就地合并两个排序数组
  5. JavaScript实现使用 BACKTRACKING 方法查找集合的幂集算法
  6. 我来说说java的NIO
  7. 使用 Log4Net 记录日志
  8. Orleans配置---持久化
  9. 依据imu姿态角计算z轴倾角_[姿态估计] DenseFusion详解
  10. 软件项目管理案例教程第4版课后习题第二章
  11. windows映射网络驱动器方法
  12. 弘玑Cyclone上榜36氪中国超自动化先锋企业
  13. 对PVP手游产品《王者荣耀》的一些思考
  14. 欧拉函数、费马定理、欧拉定理
  15. 解除360的系统压缩
  16. Java中的动态代理详解
  17. 100以内的所有质数的输出
  18. spring data JPA常用注解
  19. Jqgird 如何使用自带的search模块进行数据查询
  20. 单元格等于计算机日期,《excel表格怎样自动填写日期》 Excel单元格中自动获取当前日期与时间...

热门文章

  1. 22计算机考研上岸个人经验近万字分享(11408初试360分)
  2. python中复数的乘法_不一致的numpy复数乘法结果
  3. 机器学习项目实战——10决策树算法之动物分类
  4. DeiT:Training data-efficient image transformers distillation through attention
  5. oracle 表空间不足解决办法
  6. gcc利用-m32编译报错问题处理
  7. SPARK-SQL - group分组聚合api,agg()
  8. RT-Thread 4.1.0 特性解析之LIBC与POSIX
  9. 世界经典电影Top 50
  10. pdcp层的作用_LTE系统中PDCP子层功能研究