项目正式环境查询数据库 发现 丢失了一半的数据 ,那发生的频率就很高了, 检查死锁日志 ,
如下:
UPDATE container_dynamic
SET new_state = 0
WHERE box_code = 'xx'
分析死锁日志发现 事务1 正在等待s锁,事务2 持有 s 锁和 x 锁 no gap, 并且在等待 s锁 update 在 死锁中 的 insert 语句前执行
接下来 我们模拟 这个场景来重现 这个死锁。开启三个事务:
事务t :
BEGIN;
UPDATE `t` SET age = age+1 WHERE `name` = 'a'; -- x no gap
INSERT INTO `t` VALUES(NULL,
(SELECT a. `name` FROM (SELECT `name` FROM `t`)a WHERE a.`name` = 'a' LIMIT 1), 30);
事务t1 :
BEGIN;
UPDATE `t` SET age = age+1 WHERE `name` = 'a1'; -- x no gap
INSERT INTO `t` VALUES(NULL,
(SELECT a. `name` FROM (SELECT `name` FROM `t`)a WHERE a.`name` = 'a1' LIMIT 1), 30);
事务t2:
BEGIN;
UPDATE `t` SET age = age+1 WHERE `name` = 'a2'; -- x no gap
INSERT INTO `t` VALUES(NULL,
(SELECT a. `name` FROM (SELECT `name` FROM `t`)a WHERE a.`name` = 'a2' LIMIT 1), 30);
1: t 拿到了 lock x no gap (排他锁,只锁住了name = a 的), lock s (共享锁), insert 中select 使用了 临时表 无索引,全表扫描 , s 锁的范围是全表
2: t1 ,t2 执行 update 被 t 的 s 锁排斥 ,发生等待 x 锁,
3 : t 提交事务 ,释放 s锁,(提交过程就不贴了),t1 ,t2 update 只 x 锁住存在行 ,无 s 锁排斥 ,拿到 自己的 x 锁 ,执行 update 成功,
4: t1 执行 insert select ,t2 执行 insert select , (insert select 操作会给 select 表加上 s 锁) ,两个事务执行 select 拿到s 锁(无索引 ,锁全表)
5: t1,想执行 insert 需要 x 锁 ,但是 t2 s锁范围 为全表 ,排斥 其他事务x 锁 , t2 执行 insert 同t1 被 t1 的s 锁排斥 ,发生死锁 ,mysql 检测 回滚 权重小的 事务。
6. insert 操作最好不要带入 select 操作 特别是锁表的 ,一旦出现并发 ,死锁的几率很高。
版权声明:本文为heliangsheng123原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。