mysql 并发 insert 引起的死锁

项目正式环境查询数据库 发现 丢失了一半的数据 ,那发生的频率就很高了, 检查死锁日志  ,

如下:

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版权协议,转载请附上原文出处链接和本声明。