共计 2925 个字符,预计需要花费 8 分钟才能阅读完成。
Hello,大家好,我是 Skow
浏览这篇文章之前,大家能够问问本人
- 何为死锁?
- Mysql 具备哪些锁?
- Mysql 的锁模式兼容矩阵你是否分明?
- 如何排查死锁问题?
如果你能够闭着眼睛答复进去这些问题的,那么就默默点赞来到👍🏻
如果你对下面的知识点,还有点含糊不清,那么这篇文章将会带你从一个实在业务场景动手,剖析死锁问题,心愿本文对你有所帮忙,Let’s go 🤨
业务背景
目前我司有两个零碎 A 零碎、B 零碎
A 零碎寄存着公司所有人员的信息
B 零碎须要日终定时从 A 零碎同步数据
人员已在 B 零碎中存在,则更新,不存在则插入
因人员信息过多,所以采取多线程形式同步人员数据
在验证代码的时候,😡测试人员火冒三丈的反馈,sync\_user 也就是咱们的同步人员的那张数据表打不开了
遇事莫慌,先甩锅运维,“小姐姐,莫急莫慌,必定是数据库系统出问题了 ”
通过运维和 DBA 的排查,其实罪魁祸首是开发
咱们的代码导致了这张表呈现了死锁,从而导致表打不开了
那,到底是为何产生了死锁?接下来咱们还原一下案发现场
案发还原
看一下原始的建表语句(当然不会给你看实在的表)
CREATE TABLE `sync_user` (`user_id` VARCHAR ( 32) NOT NULL COMMENT '用户 ID',
`user_name` VARCHAR (32) DEFAULT NULL COMMENT '用户姓名',
`login_account` VARCHAR (50) DEFAULT NULL COMMENT '登陆账号',
PRIMARY KEY (`user_id`),
KEY `idx_login_account` (`login_account`) USING BTREE
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COMMENT = '用户信息表';
当初零碎中有张三、李四两个用户
表曾经准备就绪了,接下来看下咱们的数据隔离级别、并且把咱们的主动提交敞开
正戏开始!!!✍️
依照下图模仿下咱们并发同步数据的状况
- 开启 事务 1, 执行更新语句 >UPDATE sync\_user SET user\_name = “ 张三 2 ” where login\_account = “zhangsan”;
- 开启 事务 2, 执行更新语句 > UPDATE sync\_user SET user\_name = “ 李四 2 ” where login\_account = “lisi”; 更新胜利
- 回到事务 1,执行插入语句 > INSERT INTO sync\_user (user\_id, user\_name, login\_account) VALUES (‘3’, ‘ 王五 ’, ‘wangwu’);– 此条语句阻塞中
- 回到事务 2,执行插入语句 > INSERT INTO sync\_user (user\_id, user\_name, login\_account) VALUES (‘4’, ‘ 杨六 ’, ‘yangliu’); — 呈现死锁,并且事务 1 的插入语句执行胜利
以上就是咱们模仿的并发状况,课代表总结图如下 👇
死锁剖析
通过事务 2 提醒的 Deadlock found when trying to get lock; try restarting transaction
咱们能够很明确的失去,这就是产生了死锁状况
那么,什么是死锁呢?
大学老师都是这样通知咱们的:死锁是指多个过程在运行过程中因抢夺资源而造成的一种僵局,当过程处于这种僵持状态时,若无外力作用,它们都将无奈再向前推动 造成死锁的四个必要条件
- 互斥条件:一个资源每次只能被一个过程应用;
- 申请与放弃条件:一个过程因申请资源而阻塞时,对已取得的资源放弃不放;
- 不剥夺条件: 过程已取得的资源,在末应用完之前,不能强行剥夺;
- 循环期待条件: 若干过程之间造成一种头尾相接的循环期待资源关系;
ok,什么是死锁和造成死锁的必要条件咱们曾经晓得了,要产生死锁,必先有锁,那么 Mysql 有哪些锁呢?
Mysql 依照锁模式辨别有:记录锁、gap 锁、next-key 锁、插入意向锁
具体的锁作用篇幅限度,就不开展阐明
锁的兼容矩阵为:横行为以后曾经持有的锁,纵向为正在申请的锁
接下来,正式剖析一下事务 1、事务 2 各自拿到了什么锁
- 事务 1 在更新 zhangsan 张三的时候
- 间隙锁:UPDATE 语句会在非惟一索引的 login\_account 加上间隙锁,即取得 (lisi,zhangsan)、(zhangsan,+∞)
- 记录锁:因为 login\_account 为索引,会在 zhangsan 这一行加锁
- Next-Key 锁:Next-Key 锁 = 记录锁 + 间隙锁,所以该 UPDATE 语句就有了 (lisi,zhangsan] 的 Next-Key 锁
- 综上所述:更新张三的语句取得了
Next-Key 锁
-> (lisi,zhangsan]Gap 锁
-> (zhangsan,+∞)- 事务 1 在插入 wangwu 王五的时候
- 间隙锁:因为 wangwu(在 lisi 和 zhangsan 之间),所以须要申请加 (lisi,zhangsan) 的间隙锁
- 插入意向锁(Insert Intention):插入意向锁是在插入一行记录操作之前设置的一种间隙锁,这个锁开释了一种插入方式的信号,即事务 A 须要插入意向锁 (lisi,zhangsan)
因而,事务 1 的 UPDATE 语句和 INSERT 语句执行完,它是持有了 (lisi,zhangsan] 的 Next-Key 锁,(zhangsan,+∞) 的 Gap 锁,想拿到 (lisi,zhangsan) 的插入动向排它锁
事务 2 的剖析也如上举例,咱们间接给出答案
事务 2 的 UPDATE 语句和 INSERT 语句执行完,它是持有了 (-∞,lisi] 的 Next-Key 锁,(lisi,zhangsan) 的 Gap 锁,想拿到 (lisi,zhangsan) 的插入动向排它锁
锁曾经剖析结束了,接下来,咱们须要去查看一下事务的日志后果
假相行将浮出水面
事务 1 冀望拿到 (lisi,zhangsan) 的插入意向锁,然而这个范畴以后被事务 2 的 (lisi,zhangsan] 的 gap 锁占有了,这两把锁又是抵触的
事务 2 冀望拿到 (lisi,zhangsan) 的插入意向锁,然而这个范畴被事务 1 的 (lisi,zhangsan] 的 Next-Key 锁占有了,这两把锁又是抵触的
所以死锁产生。因为 Innodb 的底层机制,它会让其中一个事务让出资源,另外的事务执行胜利,这就是为什么你最初看到事务 1 插入胜利了,然而事务 2 的插入显示了 Deadlock found
总结
死锁起因曾经剖析进去了,那咱们当前面对死锁,整体解决思路是什么呢?
- 甩锅运维
- 模仿死锁场景
- show engine innodb status; 查看死锁日志
- 找出死锁 SQL
- SQL 加锁剖析,这个能够去官网看哈
- 剖析死锁日志(持有什么锁,期待什么锁)
- 相熟锁模式兼容矩阵,InnoDB 存储引擎中锁的兼容性矩阵。
参考文章:
丁奇《MySql 实战 45 讲》
捡田螺的小男孩《手把手教你剖析 Mysql 死锁问题》
文章完结 🤣
如果本文对你有所帮忙的话,那就点个赞吧
更多分享尽在微信公众号【codeLiveHouse】
公众号回复“材料”能够获取大厂面试题 / 技术文档 / 电子书等等