目录

简述

行锁导致死锁

gap lock/next keys lock导致死锁

index merge导致死锁

唯一索引冲突导致死锁

总结


简述

本文死锁场景皆为工作中遇到(或同事遇到)并解决的死锁场景,写这篇文章的目的是整理和分享,欢迎指正和补充,本文死锁场景包括:

  • 行锁导致死锁
  • gap lock/next keys lock导致死锁
  • index merge 导致死锁
  • 唯一索引冲突导致死锁

:以下场景隔离级别均为默认的Repeatable Read;

行锁导致死锁

前提:表 t_user 的 uid 字段创建了唯一索引,并拥有可更新字段age。
场景复现

行锁导致死锁

死锁原因详解

  1. 两个事务执行过程时间上有交集,并且过程发生在两者提交之前
  2. 事务1更新uid=1的记录,事务2更新uid=2的记录,在RR级别,由于uid是唯一索引,因此两个事务将分别持有uid=1和2所在行的独占锁
  3. 事务1执行到第二条更新语句时,发现uid=2的行被锁住,进入阻塞等待锁释放;
  4. 事务2执行到第二条语句时发现uid=1的行被锁,同样进入阻塞
  5. 两个事务互相等待,死锁产生。

相应业务案例和解决方案
该场景常见于事务中存在for循环更新某条记录的情况,死锁日志显示lock_mode X locks rec but not gap waiting(即行锁而非间隙锁),解决方案:

  1. 避免循环更新,优化为一条where锁定要更新的记录批量更新
  2. 如果非要循环更新,尝试取消事务(能接受的话),即每一条更新为一个独立的事务

gap lock/next keys lock导致死锁

表结构:

CREATE TABLE `t_user` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`age` int(3) DEFAULT NULL,PRIMARY KEY (`id`),KEY `udx_age` (`age`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4;

场景复现
首先查询表中目前存在的记录:

执行两个事务的操作:

死锁原因分析

  1. 事务1执行delete age = 27,务2执行delete age = 31,在RR级别,操作条件不是唯一索引时,行锁会升级为next keys lock(可以理解为间隙锁),因此事务1锁住了25到27和27到29的区间,事务2锁住了29到31的区间
  2. 事务1执行insert age = 30,等待事务2释放锁
  3. 事务2执行insert age = 28,等待事务1释放锁
  4. 死锁产生,死锁日志显示lock_mode X locks gap before rec insert intention waiting

解决方案

  1. 降低事务隔离级别到Read Committed,该隔离级别下间隙锁降级为行锁,可以减少死锁发生的概率
  2. 避免这种场景- -

index merge导致死锁

t_user结构改造为:

CREATE TABLE `t_user` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`age` int(11) DEFAULT NULL,`zone_id` bigint(20) DEFAULT NULL,`username` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`),KEY `idx_age` (`age`),KEY `idx_zone_id` (`zone_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

场景复现操作(几率不高)

假设存在以下数据

id zone_id uid username
1 1 1 ""
2 1 2 ""
3 2 1 ""
4 2 2 ""

死锁分析

  1. 在符合场景前提的情况下(即表数据量较大,index_merge未关闭),通过explain分析update t_user where zone_id = 1 and uid = 1可以发现type是index_merge,即会用到zone_id和uid两个索引
  2. 上锁的过程为:

事务1
① 锁住zone_id=1对应的间隙锁: zoneId in (1,2)
② 锁住索引zone_id=1对应的主键索引行锁id = [1,2]
③ 锁住uid=1对应的间隙锁: uid in (1, 2)
④ 锁住uid=1对应的主键索引行锁: id = [1, 3]
事务2
① 锁住zone_id=2对应的间隙锁: zoneId in (1,2)
② 锁住索引zone_id=2对应的主键索引行锁id = [3,4]
③ 锁住uid=2对应的间隙锁: uid in (1, 2)
④ 锁住uid=2对应的主键索引行锁: id = [2, 4]

  1. 如果两个事务上锁的顺序相反,则有一定的概率出现死锁。另外,index_merge的形式锁住了很多不符合条件的行,浪费了资源。一般死锁日志打印的信息为:lock_mode X locks rec but not gap waiting Record lock

解决方案:创建联合索引,使执行计划只会用到一个索引。

唯一索引冲突导致死锁

测试表结构:

CREATE TABLE `t_sample` (`id` bigint(29) NOT NULL AUTO_INCREMENT,`uid` int(11) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `uk_uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

场景复现操作

image.png

死锁分析

  1. 三个事务分别尝试插入uid=1的数据,其中事务1先于后两个事务
  2. 由于是唯一索引,所以后两个事务会出现唯一键冲突,但是事务1并未立即提交,因此不会报错,而是将事务一insert的隐式锁升级为显式锁
  3. 事务二和事务三为了判断是否出现唯一键冲突,必须进行一次当前读(select...lock in share mode),加的锁是GAP S锁,所以进入阻塞,等待事务一释放锁
  4. 事务一回滚,此时事务二和事务三成功获取记录上的GAP S锁,并继续执行插入操作
  5. 插入则需要依次请求插入意向锁,而插入意向锁和GAP S锁冲突,因此两个事务相互等待,形成死锁

解决办法:尽量避免这种插入又回滚的场景。

总结

避免死锁的原则:

  • 建立合适的索引,减小锁的粒度
  • 选择合适的事务隔离级别
  • 大事务拆成小事务,一个事务中的锁尽量少

mysql死锁场景汇总整理相关推荐

  1. 深入MySQL死锁场景

    总结死锁需满足以下条件: 2个或者2个以上的并发事务操作 并发事务之间存在锁冲突 锁冲突关系成环形 GAP锁和Insert的隐式锁,最容易导致死锁,以下分析从这俩典型场景开始. 1. 表结构 建立以下 ...

  2. mysql for update场景_一个mysql死锁场景实例分析

    前言 最近遇到一个mysql在RR级别下的死锁问题,感觉有点意思,研究了一下,做个记录. 涉及知识点:共享锁.排他锁.意向锁.间隙锁.插入意向锁.锁等待队列 场景 隔离级别:Repeatable-Re ...

  3. mysql死锁 gap next key 加锁分析

    这个案例的原文参见: mysql死锁场景汇总整理_死锁业务场景_秃了也弱了.的博客-CSDN博客 那么我们就来分析下整个加锁过程吧. 关键词: next-key & gap 锁 & 插 ...

  4. mysql 查看autocommit_手把手教你分析Mysql死锁问题

    点击上方 IT牧场 ,选择 置顶或者星标 技术干货每日送达 发生死锁了,如何排查和解决呢?本文将跟你一起探讨这个问题 准备好数据环境 模拟死锁案发 分析死锁日志 分析死锁结果 环境准备 数据库隔离级别 ...

  5. MySQL死锁解决之道

    转载自知乎 云之飞舞 真大佬 一. 了解常见的锁类型 在讨论传统的隔离级别实现的时候,我们就提到:通过对锁的类型(读锁还是写锁),锁的粒度(行锁还是表锁),持有锁的时间(临时锁还是持续锁)合理的进行组 ...

  6. mysql len hex asc_线上频出MySQL死锁问题!分享一下自己教科书般的排查和分析过程!...

    本文主要是讲过程与思路,从手上的日志来反推故障现场,最后模拟出事故现场.没有过度讲解理论的一些知识,主要是偏分析. 文章参考的理论知识在最后,同时也将本次案例提交 ISSUE 给:https://gi ...

  7. java代码大全_各种java技术文章汇总整理

    老哥简介 大家好,我是IT老哥.我大学四年读的数学专业,通过一路的自学走到今天.目前在一家大厂做高级java开发工程师.我会分享我的职场经验.java技术.java自学资料等等.我能通过自学进入大厂, ...

  8. 数据库:分享六个 MySQL 死锁案例,能让你理解死锁的原因!

    正文 最近总结了一波死锁问题,和大家分享一下,我这也是从网上各种浏览博客得来,希望原作者见谅,参考博客文末下方. Mysql 锁类型和加锁分析MySQL有三种锁的级别:页级.表级.行级. 表级锁:开销 ...

  9. mysql 死锁监控_mysql 死锁

    MySQL复制slave服务器死锁案例 原文:MySQL复制slave服务器死锁案例 MySQL复制刚刚触发了一个bug,该bug的触发条件是slave上Xtrabackup备份的时候执行flushs ...

最新文章

  1. 制作npm插件vue-toast-m实例练习
  2. Vue分支循环结构~非常详细哦
  3. tensorflow没有这个参数_TensorFlow入门笔记(五) : 神经网络参数与TensorFlow变量
  4. 试用期要盯紧你的“四金”
  5. Spring使用注解的方式实现AOP的开发——Spring AOP(七)
  6. html弹出广告设计,全屏弹出广告交互设计探讨
  7. 软件测试是做什么的?具体工作内容?
  8. 服务器ssd内存性能对比,真是大快人心 九款240/256G SSD大横评
  9. 软件工程实践 Blog17
  10. ajax请求csv文件,使用Ajax读取csv /文本文件
  11. APUE-文件和目录(六)函数ftw和nftw
  12. 30天自制操作系统-3
  13. 加那些YY主播的微信为何要花钱?
  14. 今日早报 每日精选12条新闻简报 每天一分钟 知晓天下事 9月24日
  15. 【智慧农业】智慧温室建造流程
  16. QT之qss教程- QScrollBar
  17. arm方案商,三星S5P6818开发板ARM Cortex-A53架构
  18. iPhone 14 全系售价及配置曝光,绝了!
  19. kali-linux u盘便携性系统,暗组u盘怎么进入kali linux系统
  20. matlabff2函数_罚函数法MATLAB程序.doc

热门文章

  1. spring boot 异常设计原理
  2. mybatis做批量删除时写SQL语句时遇到的问题
  3. javadoc: 错误 - 格式错误的语言环境名称_ONLYOFFICE 5.6.0 : 这是一个错误修正版本,改进了德语、法语、意大利语、葡萄牙语和俄语的翻译等...
  4. python信号处理教程_python 之信号Signal|python3教程|python入门|python教程
  5. 三菱modbusRTU通讯实例_干货 | 解析西门子系列PLC编程实例
  6. python 数值运算 m op n_python数值运算 四则运算
  7. 1、leetcode437 路和总径3
  8. matlab硬接触,abaqus中的关于硬接触(Hard contact)、及其他接触
  9. python修改数据库_python mysql修改数据库数据库
  10. python心跳的实现_(python)面向对象