【MySQL】数据库死锁问题排查定位

数据库死锁问题排查定位

  • 一、产生死锁的条件
  • 二、排查数据库死锁
  • 三、死锁解决办法
    • 3.1 一个事务中多个SQL访问多张表产生死锁
    • 3.2 重新定义索引
    • 3.3 降低事务隔离级别

一、产生死锁的条件

如果我们的数据库并发请求不高一般不会产生死锁。即便存在高并发,也不一定会产生死锁。只有满足一定的条件才会产生死锁,即死锁产生的4个必要条件:

  • 资源排它性。一个资源(数据)只能被一个事务占有;
  • 不可剥夺性。一个事务在占有一份资源期间,该资源不能再被其它事务剥夺;
  • 请求与保持。一个事务在占有一份资源的同时,继续保持请求其它的资源;
  • 循环等待。多个事务在保持占有自己的资源,同时请求其他资源构成了一个环;

二、排查数据库死锁

  1. 首先,确定所使用的数据库的版本:
select version()

【MySQL】数据库死锁问题排查定位_第1张图片
2. 确定数据表使用的存储引擎及表结构信息,以我本地一个数据表为例:

show create table `students`;

【MySQL】数据库死锁问题排查定位_第2张图片
3. 确定当前数据库中事务的隔离级别:

# mysql 8.x 版本
select @@transaction_isolation;
# mysql 5.x 版本
select @@tx_isolation;

【MySQL】数据库死锁问题排查定位_第3张图片
4. 查看死锁日志。同时,我们应该明确一点:数据库表中对字段进行加锁,锁的是索引,所以下面我们下面在查看死锁日志时应该留意加了索引的字段。

  • MySQL5.6及之前的版本可以通过 show engine innodb status 命令来查看最近发生的死锁的死锁日志。可以看到那几个SQL执行形成了死锁,以及他们分别持有哪个锁,在等待哪个锁等;
  • MySQL5.6以上的版本可以通过参数 innodb_print_all_deadlocks 对MySQL进行配置,决定着MySQL 是否开启死锁日志;
# 查询MySQL死锁日志开关是否开启
show variables like 'innodb_print_all_deadlocks';
# 开启MySQL死锁日志
set global innodb_print_all_deadlocks=1;

【MySQL】数据库死锁问题排查定位_第4张图片

如果开启了死锁日志,死锁日志就会被输出到MySQL的错误日志中,通过 show variables like 'log_error%' 查看MySQL错误日志的路径,在对应的错误日志路径下查看对应的文件内容。

【MySQL】数据库死锁问题排查定位_第5张图片

三、死锁解决办法

3.1 一个事务中多个SQL访问多张表产生死锁

  • 看是否可以指定获取表上记录的顺序。例如将业务上使用的所有表都设置一个序号,当在一个事务中执行多个SQL ,要获取到多个锁时,我们按照表序号的顺序获取锁,如果可以获得所有的锁则提交该事务,否则立刻回滚。
  • 当然,要尽量避免长事务,不推荐在一个事务里执行多个SQL。

3.2 重新定义索引

  • 需明确:数据库上的锁是加在索引上的。所以,当索引的区分度不够的时候,一个索引值可能会对应很多的数据,这也意味着如果一个事务对一条索引值加了锁,这个索引值对应记录的请求事务都可能被锁住。这很大程度提高了死锁发生的概率。

3.3 降低事务隔离级别

  • 如果数据库并发度较高的话,可通过降低事务的隔离级别进一步提高并发。MySQL的默认隔离级别是 Repeatable Read,如果业务场景上较少发生幻读或者对幻读容忍度高,可将事务的隔离级别调整到Read Committed

你可能感兴趣的:(数据库,mysql)