我的编程空间,编程开发者的网络收藏夹
学习永远不晚

MySQL死锁举例分析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

MySQL死锁举例分析

这篇文章主要介绍“MySQL死锁举例分析”,在日常操作中,相信很多人在MySQL死锁举例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”MySQL死锁举例分析”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

一 前言
  死锁,其实是一个很有意思,也很有挑战的技术问题,大概每个DBA和部分开发同学都会在工作过程中遇见过 。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋友有所帮助。本文介绍一例三个并发insert 导致的死锁,根本原因还是在于insert 唯一键申请插入意向锁这个特殊的GAP锁。其实称呼插入意向锁 为 Insert Intention Gap Lock 更为合理。
二 案例分析
2.1 环境准备 
Percona server 5.6 RR模式

sess1

sess2

sess3


begin;



insert into t6(id,a) values(6,15);

begin;



insert into t6(id,a) values(7,15);

begin;



insert into t6(id,a) values(8,15);

rollback;


ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction



2.2 死锁日志

  1. ------------------------

  2. LATEST DETECTED DEADLOCK

  3. ------------------------

  4. 2017-09-18 10:03:50 7f78eae30700

  5. *** (1) TRANSACTION:

  6. TRANSACTION 462308725, ACTIVE 18 sec inserting, thread declared inside InnoDB 1

  7. mysql tables in use 1, locked 1

  8. LOCK WAIT 4 lock struct(s), heap size 1184, 2 row lock(s), undo log entries 1

  9. MySQL thread id 3825465, OS thread handle 0x7f78eaef4700, query id 781148519 localhost root update

  10. insert into t6(id,a) values(7,15)

  11. *** (1) WAITING FOR THIS LOCK TO BE GRANTED:

  12. RECORD LOCKS space id 227 page no 4 n bits 80 index `idx_a` of table `test`.`t6` trx id 462308725 lock_mode X insert intention waiting

  13. *** (2) TRANSACTION:

  14. TRANSACTION 462308726, ACTIVE 10 sec inserting, thread declared inside InnoDB 1

  15. mysql tables in use 1, locked 1

  16. 4 lock struct(s), heap size 1184, 2 row lock(s), undo log entries 1

  17. MySQL thread id 3825581, OS thread handle 0x7f78eae30700, query id 781148528 localhost root update

  18. insert into t6(id,a) values(8,15)

  19. *** (2) HOLDS THE LOCK(S):

  20. RECORD LOCKS space id 227 page no 4 n bits 80 index `idx_a` of table `test`.`t6` trx id 462308726 lock mode S

  21. *** (2) WAITING FOR THIS LOCK TO BE GRANTED:

  22. RECORD LOCKS space id 227 page no 4 n bits 80 index `idx_a` of table `test`.`t6` trx id 462308726 lock_mode X insert intention waiting

  23. *** WE ROLL BACK TRANSACTION (2)

2.3 死锁分析
首先依然要再次强调insert 插入操作的加锁逻辑。
第一阶段: 唯一性约束检查,先申请LOCK_S + LOCK_ORDINARY
第二阶段: 获取阶段一的锁并且insert成功之后,插入的位置有Gap锁:LOCK_INSERT_INTENTION,为了防止其他insert唯一键冲突。
               新数据插入:LOCK_X + LOCK_REC_NOT_GAP
对于insert操作来说,若发生唯一约束冲突,则需要对冲突的唯一索引加上S Next-key Lock。从这里会发现,即使是RC事务隔离级别,也同样会存在Next-Key Lock锁,从而阻塞并发。然而,文档没有说明的是,对于检测到冲突的唯一索引,等待线程在获得S Lock之后,还需要对下一个记录进行加锁,在源码中由函数row_ins_scan_sec_index_for_duplicate进行判断.
其次 我们需要了解 锁的兼容性矩阵。
MySQL死锁举例分析
从兼容性矩阵我们可以得到如下结论:

  1. INSERT操作之间不会有冲突。

  2. GAP,Next-Key会阻止Insert。

  3. GAP和Record,Next-Key不会冲突

  4. Record和Record、Next-Key之间相互冲突。

  5. 已有的Insert锁不阻止任何准备加的锁。

这个案例是三个会话并发执行的,我打算一步一步来分析每个步骤执行完之后的事务日志。
第一步 sess1 执行插入操作
insert into t6(id,a) values(6,15);

  1. ---TRANSACTION 462308737, ACTIVE 5 sec

  2. 1 lock struct(s), heap size 360, 0 row lock(s), undo log entries 1

  3. MySQL thread id 3825779, OS thread handle 0x7f78eacd9700, query id 781149440 localhost root init

  4. show engine innodb status

  5. TABLE LOCK table `test`.`t6` trx id 462308737 lock mode IX

因为第一个插入的语句,所以唯一性冲突检查通过,成功插入(6,15). 此时sess1 会话持有(6,15)的LOCK_X|LOCK_REC_NOT_GAP锁。参考"INSERT sets an exclusive lock on the inserted row. This lock is an index-record lock, not a next-key lock (that is, there is no gap lock) and does not prevent other sessions from inserting into the gap before the inserted row."

第二步 sess2 执行插入操作
insert into t6(id,a) values(7,15);

  1. ---TRANSACTION 462308738, ACTIVE 4 sec inserting

  2. mysql tables in use 1, locked 1

  3. LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s), undo log entries 1

  4. MySQL thread id 3825768, OS thread handle 0x7f78ea9c9700, query id 781149521 localhost root update

  5. insert into t6(id,a) values(7,15)

  6. ------- TRX HAS BEEN WAITING 4 SEC FOR THIS LOCK TO BE GRANTED:

  7. RECORD LOCKS space id 227 page no 4 n bits 80 index `idx_a` of table `test`.`t6` trx id 462308738 lock mode S waiting

  8. ------------------

  9. TABLE LOCK table `test`.`t6` trx id 462308738 lock mode IX

  10. RECORD LOCKS space id 227 page no 4 n bits 80 index `idx_a` of table `test`.`t6` trx id 462308738 lock mode S waiting

  11. ---TRANSACTION 462308737, ACTIVE 66 sec

  12. 2 lock struct(s), heap size 360, 1 row lock(s), undo log entries 1

  13. MySQL thread id 3825779, OS thread handle 0x7f78eacd9700, query id 781149526 localhost root init

  14. show engine innodb status

  15. TABLE LOCK table `test`.`t6` trx id 462308737 lock mode IX

  16. RECORD LOCKS space id 227 page no 4 n bits 80 index `idx_a` of table `test`.`t6` trx id 462308737 lock_mode X locks rec but not gap

首先sess2的insert 申请了IX锁,因为sess1 会话已经插入成功并且持有唯一键 a=15的X 行锁 ,故而sess2 insert 进行唯一性检查,先申请LOCK_S + LOCK_ORDINARY ,事务日志列表中提示lock mode S waiting
第三部 sess3 执行插入操作
insert into t6(id,a) values(8,15);

  1. ---TRANSACTION 462308739, ACTIVE 3 sec inserting

  2. mysql tables in use 1, locked 1

  3. LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s), undo log entries 1

  4. MySQL thread id 3825764, OS thread handle 0x7f78ea593700, query id 781149555 localhost root update

  5. insert into t6(id,a) values(8,15)

  6. ------- TRX HAS BEEN WAITING 3 SEC FOR THIS LOCK TO BE GRANTED:

  7. RECORD LOCKS space id 227 page no 4 n bits 80 index `idx_a` of table `test`.`t6` trx id 462308739 lock mode S waiting

  8. ------------------

  9. TABLE LOCK table `test`.`t6` trx id 462308739 lock mode IX

  10. RECORD LOCKS space id 227 page no 4 n bits 80 index `idx_a` of table `test`.`t6` trx id 462308739 lock mode S waiting

  11. ---TRANSACTION 462308738, ACTIVE 35 sec inserting

  12. mysql tables in use 1, locked 1

  13. LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s), undo log entries 1

  14. MySQL thread id 3825768, OS thread handle 0x7f78ea9c9700, query id 781149521 localhost root update

  15. insert into t6(id,a) values(7,15)

  16. ------- TRX HAS BEEN WAITING 35 SEC FOR THIS LOCK TO BE GRANTED:

  17. RECORD LOCKS space id 227 page no 4 n bits 80 index `idx_a` of table `test`.`t6` trx id 462308738 lock mode S waiting

  18. ------------------

  19. TABLE LOCK table `test`.`t6` trx id 462308738 lock mode IX

  20. RECORD LOCKS space id 227 page no 4 n bits 80 index `idx_a` of table `test`.`t6` trx id 462308738 lock mode S waiting

  21. ---TRANSACTION 462308737, ACTIVE 97 sec

  22. 2 lock struct(s), heap size 360, 1 row lock(s), undo log entries 1

  23. MySQL thread id 3825779, OS thread handle 0x7f78eacd9700, query id 781149560 localhost root init

  24. show engine innodb status

  25. TABLE LOCK table `test`.`t6` trx id 462308737 lock mode IX

  26. RECORD LOCKS space id 227 page no 4 n bits 80 index `idx_a` of table `test`.`t6` trx id 462308737 lock_mode X locks rec but not gap

与会话sess2 的加锁申请流程一致,都在等待sess1释放锁资源。
第四步 sess1 执行回滚操作,sess2 不提交
sess1 rollback;
此时sess2 插入成功,sess3出现死锁,此时sess2 insert插入成功,还未提交,事务列表如下:

  1. ------------

  2. TRANSACTIONS

  3. ------------

  4. Trx id counter 462308744

  5. Purge done for trx s n:o < 462308744 undo n:o < 0 state: running but idle

  6. History list length 1866

  7. LIST OF TRANSACTIONS FOR EACH SESSION:

  8. ---TRANSACTION 462308737, not started

  9. MySQL thread id 3825779, OS thread handle 0x7f78eacd9700, query id 781149626 localhost root init

  10. show engine innodb status

  11. ---TRANSACTION 462308739, not started

  12. MySQL thread id 3825764, OS thread handle 0x7f78ea593700, query id 781149555 localhost root cleaning up

  13. ---TRANSACTION 462308738, ACTIVE 75 sec

  14. 5 lock struct(s), heap size 1184, 3 row lock(s), undo log entries 1

  15. MySQL thread id 3825768, OS thread handle 0x7f78eadce700, query id 781149608 localhost root cleaning up

  16. TABLE LOCK table `test`.`t6` trx id 462308738 lock mode IX

  17. RECORD LOCKS space id 227 page no 4 n bits 80 index `idx_a` of table `test`.`t6` trx id 462308738 lock mode S

  18. RECORD LOCKS space id 227 page no 4 n bits 80 index `idx_a` of table `test`.`t6` trx id 462308738 lock mode S

  19. RECORD LOCKS space id 227 page no 4 n bits 80 index `idx_a` of table `test`.`t6` trx id 462308738 lock_mode X insert intention

  20. RECORD LOCKS space id 227 page no 4 n bits 80 index `idx_a` of table `test`.`t6` trx id 462308738 lock mode S locks gap before rec

死锁的原因
 sess1 insert成功并针对a=15的唯一键加上X锁。
 sess2 执行insert 插入(6,15), 在插入之前进行唯一性检查发现和sess1的已经插入的记录重复键需要申请LOCK_S|LOCK_ORDINARY, 但与sess1 的(LOCK_X | LOCK_REC_NOT_GAP)冲突,加入等待队列,等待sess1 释放锁。
 sess3 执行insert 插入(7,15), 在插入之前进行唯一性检查发现和sess1的已经插入的记录重复键需要申请LOCK_S|LOCK_ORDINARY, 但与sess1 的(LOCK_X | LOCK_REC_NOT_GAP)冲突,加入等待队列,等待sess1 释放锁。
 sess1 执行rollback, sess1 释放索引a=15 上的排他记录锁(LOCK_X | LOCK_REC_NOT_GAP),此后 sess2和sess3 获得S锁(LOCK_S|LOCK_ORDINARY)成功,sess2和sess3都要请求索引a=15上的排他记录锁(LOCK_X | LOCK_REC_NOT_GAP),日志中提示 lock_mode X insert intention。由于X锁与S锁互斥,sess2和sess3都等待对方释放S锁,于是出现死锁,MySQL 选择回滚其中之一。

到此,关于“MySQL死锁举例分析”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

MySQL死锁举例分析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

MySQL死锁案例分析

最近项目中某个模块稳定复现MySQL死锁问题,本文记录死锁的发生原因以及解决办法。1. 预备知识1.1 表锁和行锁表锁表锁是MySQL中最基本的锁策略,并且是开销最小的策略。表锁会锁定整张数据表,用户的写操作(插入/删除/更新)前,都需要获取写锁(写锁会相互阻
MySQL死锁案例分析
2015-02-06

Linux死锁实例分析

这篇“Linux死锁实例分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Linux死锁实例分析”文章吧。死锁简介进程(线程
2023-06-28

MySQL锁等待与死锁问题分析

前言: 在 MySQL 运维过程中,锁等待和死锁问题是令各位 DBA 及开发同学非常头痛的事。出现此类问题会造成业务回滚、卡顿等故障,特别是业务繁忙的系统,出现死锁问题后影响会更严重。本篇文章我们一起来学习下什么是锁等待及死锁,出现此类问
2022-05-22

MySQL 核心模块—揭秘死锁案例分析插入意向锁

没有其它事务在等待获得这条记录的锁,事务 1 想要往这条记录前面的间隙插入记录,不需要等待获得插入意向锁,可以直接插入记录。

MySQL数据库之Purge死锁问题的示例分析

小编给大家分享一下MySQL数据库之Purge死锁问题的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!Purge死锁场景说明Purge死锁说明表中存在记录(unique key) 10,20,30,40 (且有 自
2023-05-30

MySQL死锁系列-常见加锁场景分析

本文我们就从原理走向实战,分析常见 SQL 语句的加锁场景。了解了这几种场景,相信小伙伴们也能举一反三,灵活地分析真实开发过程中遇到的加锁问题。

java并发编程死锁定义及避免死锁案例分析

这篇文章主要介绍“java并发编程死锁定义及避免死锁案例分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“java并发编程死锁定义及避免死锁案例分析”文章能帮助大家解决问题。场景模拟分析场景一:狭路
2023-06-29

MySQL产生死锁原因分析讲解

死锁指的是在两个或两个以上不同的进程或线程中,由于存在共同资源的竞争或进程(或线程)间的通讯而导致各个线程间相互挂起等待,如果没有外力作用,最终会引发整个系统崩溃
2022-12-16

编程热搜

目录