Mysql并发常见的死锁及解决方法

 更新时间:2023年12月11日 15:26:31   作者:cwh_hot  
死锁是在并发执行的过程中,两个或多个事务相互等待对方释放资源的情况,本文主要介绍了Mysql并发常见的死锁及解决方法,具有一定的参考价值,感兴趣的可以了解一下

使用数据库时,有时会出现死锁。对于实际应用来说,就是出现系统卡顿。

死锁是指两个或两个以上的事务在执行过程中,因争夺资源而造成的一种互相等待的现象。就是所谓的锁资源请求产生了回路现象,即死循环,此时称系统处于死锁状态或系统产生了死锁。常见的报错信息为“Deadlock found when trying to get lock...”。

上图中,很明显是右侧的四辆汽车造成了死锁。

死锁发生以后,只有部分或完全回滚其中一个事务,才能打破死锁。多数情况下只需要重新执行因死锁回滚的事务即可。下面我们通过一个实例来了解死锁是如何产生的。

例 1

为了方便读者阅读,操作之前我们先查询 tb_student 表的数据和表结构。

mysql> SELECT * FROM tb_student;
+----+------+------+------+------+
| id | name | age  | sex  | num  |
+----+------+------+------+------+
|  1 | 张三 |   31 | 男   |    4 |
|  2 | 李四 |   28 | 男   |    4 |
|  3 | 王五 |   13 | 女   |    4 |
|  4 | 张四 |   13 | 女   |    4 |
|  5 | 王四 |   15 | 男   |    4 |
|  6 | 赵六 |   12 | 女   |    4 |
+----+------+------+------+------+
6 rows in set (0.01 sec)

mysql> DESC tb_student;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(4)      | NO   | PRI | NULL    | auto_increment |
| name  | varchar(25) | NO   |     | NULL    |                |
| age   | int(11)     | YES  | MUL | NULL    |                |
| sex   | char(1)     | YES  |     | NULL    |                |
| num   | int(11)     | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

以下操作需要打开两个会话窗口,即下面所提到的 A窗口和 B窗口。

在 A窗口中执行以下命令:

mysql> BEGIN;
mysql> UPDATE tb_student SET num=5 WHERE age=13;
Query OK, 2 rows affected (0.04 sec)
Rows matched: 2  Changed: 2  Warnings: 0

紧接着在 B窗口中执行以下命令。由于 age 是索引字段,与 A窗口中更新的是不同行的数据,所以这时不会出现锁等待现象。

mysql> BEGIN;
mysql> UPDATE tb_student SET num=8 WHERE age=15;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

然后在 A窗口中,执行以下命令,这时就会出现锁等待现象了。

mysql> UPDATE tb_student SET num=10 WHERE age=15;

最后在 B窗口中,执行以下命令,这时会出现相互等待资源的现象,也就是死锁现象。

mysql> UPDATE tb_student SET num=12 WHERE age=13;
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

我们可以通过 SHOW ENGINE INNODB STATUS 命令查看死锁的信息,运行结果如下(这里只展示了部分信息):

LATEST DETECTED DEADLOCK
------------------------
2020-08-24 16:22:23 0x3944
*** (1) TRANSACTION:
TRANSACTION 22656, ACTIVE 108 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 5 lock struct(s), heap size 1136, 6 row lock(s), undo log entries 2
MySQL thread id 33, OS thread handle 8808, query id 1689 localhost ::1 root updating
UPDATE tb_student SET num=10 WHERE age=15
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 197 page no 8 n bits 80 index index_age of table `test`.`tb_student` trx id 22656 lock_mode X waiting
Record lock, heap no 5 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 8000000f; asc     ;;
1: len 4; hex 80000005; asc     ;;
......

通过以上日志,我们就能确定造成死锁的事务和 SQL 语句。

死锁检测

InnoDB 的并发写操作会触发死锁,同时 InnoDB 也提供了死锁检测机制。通过设置 innodb_deadlock_detect 参数的值来控制是否打开死锁检测。

  • innodb_deadlock_detect = ON :默认值,打开死锁检测。数据库发生死锁时,系统会自动回滚其中的某一个事务,让其它事务可以继续执行。
  • innodb_deadlock_detect = OFF:关闭死锁检测。发生死锁时,系统会用锁等待来处理。

锁等待是指在事务过程中产生的锁,其它事务需要等待上一个事务释放锁,才能占用该资源。如果该事务一直不释放,就需要持续等待下去,直到超过了锁等待时间。当超过锁等待允许的最大时间,就会出现死锁,然后当前事务执行失败,自动执行回滚操作。

MySQL 通过 innodb_lock_wait_timeout 参数控制锁等待的时间,单位是秒。

mysql> SHOW VARIABLES LIKE '%innodb_lock_wait%';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 120   |
+--------------------------+-------+
1 row in set, 1 warning (0.02 sec)

在实际应用中,我们要尽量防止锁等待现象的发生,下面介绍几种避免死锁的方法:

  • 如果不同程序会并发存取多个表,或者涉及多行记录时,尽量约定以相同的顺序访问表,这样可以大大降低死锁的发生。
  • 业务中要及时提交或者回滚事务,可减少死锁产生的概率。
  • 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率。
  • 对于非常容易产生死锁的业务部分,可以尝试使用升级锁粒度,通过表锁定来减少死锁产生的概率(表级锁不会产生死锁)。

到此这篇关于Mysql并发常见的死锁及解决方法的文章就介绍到这了,更多相关Mysql并发死锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • mysql 无限级分类实现思路

    mysql 无限级分类实现思路

    关于该问题,暂时自己还没有深入研究,在网上找到几种解决方案,各有优缺点。
    2011-08-08
  • mysql中varchar和text的区别和比较

    mysql中varchar和text的区别和比较

    在存储字符串时可以使用char、varchar或者text类型,下面这篇文章主要给大家介绍了关于mysql中varchar和text的区别和比较的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2023-10-10
  • MySQL数据库给表添加索引的实现

    MySQL数据库给表添加索引的实现

    在MySQL中,索引是用来加速数据库查询的一种特殊数据结构,当我们需要查询数据库中某些数据的时候,如果数据库中有索引,就可以避免全表扫描,从而提高查询速度,本文就介绍了如何给表添加索引,感兴趣的可以了解一下
    2023-08-08
  • Mysql5.7如何修改root密码

    Mysql5.7如何修改root密码

    mysql修改管理员root的密码是个很常见的问题了,网上也有很多的教程,然而新版的MYSQL5.7却能使用之前的教程,小编经过一番摸索,才找到了修改办法,这里分享给大家。
    2016-01-01
  • MySQL大小写敏感的注意事项

    MySQL大小写敏感的注意事项

    MySQL 在 Windows 下不区分大小写,但在 Linux 下默认是区分大小写。如果你稍加不注意就会出现在本机开发的程序运行一切正常,发布到服务器行就出现表名找不到的问题,一头雾水。
    2021-05-05
  • MySQL如何快速创建800w条测试数据表

    MySQL如何快速创建800w条测试数据表

    这篇文章主要介绍了MySQL如何快速创建800w条测试数据表,下面文章围绕MySQL创建测试数据表的相关资料展开详细内容,具有一的的参考价值,需要的小伙伴可以参考一下
    2022-03-03
  • Mysql中的索引精讲

    Mysql中的索引精讲

    这篇文章主要给大家介绍了关于Mysql中索引的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Mysql具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-05-05
  • 生产环境MySQL索引时效的排查过程

    生产环境MySQL索引时效的排查过程

    这篇文章主要介绍了生产环境MySQL索引时效的排查过程,文章根据SQL查询耗时特别长,看了执行计划发现没有走索引的问题展开详细介绍,需要的朋友可以参考一下
    2022-04-04
  • MySQL如何创建视图

    MySQL如何创建视图

    这篇文章主要介绍了MySQL如何创建视图,帮助大家更好的理解和学习MySQL,感兴趣的朋友可以了解下
    2020-08-08
  • mysql实现设置定时任务的方法分析

    mysql实现设置定时任务的方法分析

    这篇文章主要介绍了mysql实现设置定时任务的方法,结合实例形式分析了mysql定时任务相关的事件计划设置与存储过程使用等操作技巧,需要的朋友可以参考下
    2019-10-10

最新评论