Mysql死锁的问题

Mysql死锁的问题

1,先看错误日志

WechatIMG21 WechatIMG22

从以上日志可以看出是两条SQL执行出现了问题,后面一条SQL回滚了

SQL为:

update dynamic_check_packet_system set check_flag = '3' where create_time <= '2018-12-19 02:00:00' and check_flag='0' and conflict_field not in ('suspectCancel');
update flight.dynamic_check_packet_system set check_flag='1', update_time='2018-12-19 09:55:00.0',check_start_time='2018-12-19 10:00:00' where id=14211034;

使用explain查看第一条SQL

E9C461C524DED9A648DC4DCFF089919F

可以看到,这个语句使用了idxcheckflag这个索引,createtime在前,为什么没有使用createtime?

查看表中<=createtime的数据,发现有14166149条,远远大于checkflag=0的记录数,这时MySQL就会优先选择使用chekflag的索引,所以第一条语句会把checkflag=0的所有记录数都锁住。

第二条SQL同样更新check_flag=0,id=14211034的记录,但这条记录是被第一条SQL锁住的,所以就会更新失败了?

深入分析:
这里查看MySQL引擎,用的是Innodb,Innodb是支持行锁的,既然第一条SQL把这条记录行锁住了,第二条SQL应该等待才对,为什么会发生死锁呢?所以这里一定存在两把锁,而且锁的顺序不同。

我们知道MySQL的Innodb主键使用了聚集索引(索引直接指向实际数据),而如果再新建一个索引,这个索引会指向主键索引,然后通过主键索引找到数据,所以这里存在需要更新聚集索引ID数据和二级索引check_flag数据

第一条语句通过checkflag=0查找,那么就先会锁住二级索引checkflag数据,然后再去获取聚集索引ID数据的锁

而第二天SQL则是通过ID查找,那么就会先会锁住聚集索引ID数据,然后再去获取二级索引check_flag数据

显然这样获取锁的先后顺序不同,就造成了死锁。

问题解决:
让第一条SQL语句使用createtime索引,可以指定一个createtime>’开始时间’ and createtime<‘结束时间’来减少扫描行数,让MySQL优先使用createtime索引。

 

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注