MySQL 按照主键更新报Deadlock错误原因分析

这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战

问题描述

报错信息很简单在执行update操作语句的时候报错。

Deadlock found when trying to get lock; try restarting transaction; nested exception is com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Deadlock found when trying to get lock;

lQLPDhrgKxz8mX3NAe_NBb6wVHF5PgQasK8BnIUznEBYAA_1470_495.png

MySQL锁介绍

MySQL有三种锁的级别:页级、表级、行级

image.png

  1. 行级锁在使用的时候并不是直接锁掉这行记录,而是锁索引
  2. 如果一条sql用到了主键索引(mysql主键自带索引), mysql会锁住主键索引;
  3. 如果一条sql操作了非主键索引,mysql会先锁住非主键索引,再锁定主键索引.

原因分析

报错的原因很简单,我们在按照主键更新表记录的时候,SQL抛了一个死锁的错误。
那就很奇怪按照主键索引更新按道理不会产生错误, 我们观察报错的时间几次都是在凌晨3点左右的时候抛的异常。 3点的时候我们会将这个表的数据做备份,执行delete删除过期的数据,删除数据的时候索引。

我们先看第一个更新的SQL, 一点毛病没有,先查询出来主键ID,然后更新记录

image.png

然后看一眼这张表的索引, 也很正常

image.png

排除更新代码问题,那么肯定是有其他地方更新非主键索引导致的行锁级别提升导致死锁。注意时间点每次都是凌晨3点钟, 是有一个定时任务备份历史记录的操作。

image.png

我们可以看到send_time 是非主键索引,当定时任务更新的时候,这个时候在操作更新操作会导致死锁。

参考

最后推荐一个Github维护的一个解决死锁案例汇总的一个项目。
github.com/aneasystone…

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%