Mysql 死锁

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: 在高并发情况下,不同事务持有资源而等待另一个资源锁时,而其他事务恰好相反,出现了循环等待,即出现了死锁。

在高并发情况下,不同事务持有资源而等待另一个资源锁时,而其他事务恰好相反,出现了循环等待,即出现了死锁。
一般的,最常见且最易理解的死锁形式是:
事务T1持有A的锁->等待B的锁
事务T2持有B的锁->等待A的锁
这样的形式即发生了死锁,后续会介绍死锁避免及死锁解决,现在来看看对同一个行操作,mysql是如何出现死锁了。

首先,T1进行

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT id FROM pet where id=10 for share;
+----+
| id |
+----+
| 10 |
+----+
1 row in set (0.00 sec)

T2进行

START TRANSACTION;
DELETE from pet where id=10;
##会提示
DELETE from pet where id=10;
[Err] 1205 - Lock wait timeout exceeded; try restarting transaction

因为T1不是自动提交事务,查询的时候,hold持有共享锁,而此时T2开启事务并试图进行delete拿x锁,但处于一直等待中,当最后等待锁超时而自动放弃(innodb-lock-wait-timeout 默认是50s)。

做一下调整,如果T2还在等待T1释放锁时,T1进行同样的删除操作
T1:

mysql> DELETE from pet where id=10;
Query OK, 1 row affected (0.00 sec)

此时 T2:

DELETE from pet where id=10;
[Err] 1213 - Deadlock found when trying to get lock; try restarting transaction

过程:如果T1开启事务,先拿取了记录R的S读锁,此时T2来删除R需要用X锁,因T1占有S锁不得不等待,在此等待过程中,T1又进行了删除操作又尝试对R记录追加X锁,最后导致T2发生死锁退出。

死锁原因:
此处发生死锁,因为客户端T1需要X锁来删除该行。 但是,无法授予该锁请求,因为客户端T2已经有一个X锁请求并且正在等待客户端T1释放其S锁。 由于T2先前对X锁的请求,T1保持的S锁也不能升级到X锁。 因此,InnoDB会为其中一个客户端生成错误并释放锁。

启用死锁检测(默认设置)后,InnoDB会自动检测事务死锁并回滚事务或事务以打破死锁。 InnoDB尝试选择要回滚的小事务,其中事务的大小由插入,更新或删除的行数决定。
innodb-deadlock-detect[={OFF|ON}] 死锁检测,默认值为ON

如果innodb_table_locks = 1(默认值)和autocommit = 0,InnoDB知道表锁,并且它上面的MySQL层知道行级锁。否则,InnoDB无法检测到由MySQL LOCK TABLES语句设置的表锁或由InnoDB以外的存储引擎设置的锁的死锁。通过设置innodb_lock_wait_timeout系统变量的值来解决这些情况。

当InnoDB执行事务的完全回滚时,将释放由事务设置的所有锁。但是,如果由于错误而仅回滚单个SQL语句,则可能会保留由该语句设置的某些锁定。发生这种情况是因为InnoDB以一种格式存储行锁,以至于后来无法知道哪个锁由哪个语句设置。

如果SELECT在事务中调用存储的函数,并且函数中的语句失败,则该语句将回滚。此外,如果在此之后执行ROLLBACK,则整个事务回滚。

禁用死锁检测
在高并发系统上,当许多线程等待同一个锁时,死锁检测会导致速度减慢。有时,在发生死锁时,禁用死锁检测并依赖innodb_lock_wait_timeout设置进行事务回滚可能更有效。可以使用innodb_deadlock_detect配置选项禁用死锁检测。

使用以下技术处理死锁并降低其发生的可能性:
●在任何时候,发出SHOW ENGINE INNODB STATUS命令以确定最近死锁的原因。这可以帮助您调整应用程序以避免死锁。

●如果频繁出现死锁警告,请通过启用innodb_print_all_deadlocks配置选项来收集更多的调试信息。有关每个死锁的信息,而不仅仅是最新的死锁,都记录在MySQL错误日志中。完成调试后禁用此选项。

●如果由于死锁而失败,请始终准备重新发布事务。死锁并不危险。再试一次。

●保持交易持续时间短且不易发生,以减少交易。

●在进行一组相关更改后立即提交事务,以使它们不易发生冲突。特别是,不要使用未提交的事务使交互式mysql会话长时间保持打开状态。

●如果使用锁定读取(SELECT ... FOR UPDATE或SELECT ... FOR SHARE),请尝试使用较低的隔离级别,例如READ COMMITTED。

●在事务中修改多个表或同一个表中的不同行集时,每次都以一致的顺序执行这些操作。然后事务形成定义良好的队列,不会死锁。例如,将数据库操作组织到应用程序中的函数中,或调用存储的过程,而不是在不同的位置编写多个类似的INSERT,UPDATE和DELETE语句序列。

●在表中添加精心选择的索引。然后,您的查询需要扫描更少的索引记录,从而设置更少的锁。使用EXPLAIN SELECT确定MySQL服务器认为哪些索引最适合您的查询。

●使用较少的锁。如果您能够允许SELECT从旧快照返回数据,请不要向其添加FOR UPDATE或FOR SHARE子句。在这里使用READ COMMITTED隔离级别很好,因为同一事务中的每个一致读取都从其自己的新快照读取。

●如果没有其他帮助,请使用表级锁定序列化您的事务。将LOCK TABLES用于事务表(如InnoDB表)的正确方法是使用SET autocommit = 0(不是START TRANSACTION)后跟LOCK TABLES开始事务,并且在显式提交事务之前不调用UNLOCK TABLES。例如,如果需要写入表t1并从表t2读取,则可以执行以下操作:

SET autocommit = 0;
LOCK TABLES t1 WRITE,t2 READ,...;
...... do something with tables t1 and t2 here......
COMMIT;
UNLOCK TABLES;

表级锁可防止对表的并发更新,从而避免死锁,但代价是对繁忙系统的响应性较低。

●序列化事务的另一种方法是创建一个只包含一行的辅助“信号量”表。让每个事务在访问其他表之前更新该行。这样,所有事务都以串行方式发生。请注意,InnoDB即时死锁检测算法在这种情况下也适用,因为序列化锁是一个行级锁。使用MySQL表级锁定时,必须使用超时方法来解决死锁。

补:
你可以在my.ini配置文件中,使用innodb_read_io_threads和innodb_write_io_threads配置参数来配置为数据页读写I/O提供服务的后台线程的数量。这些参数表示用于读写请求的后台线程的数量。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
1月前
|
SQL 关系型数据库 MySQL
MySQL死锁及源码分析!
MySQL死锁及源码分析!
MySQL死锁及源码分析!
|
1月前
|
SQL 关系型数据库 MySQL
案例剖析:MySQL唯一索引并发插入导致死锁!
案例剖析:MySQL唯一索引并发插入导致死锁!
案例剖析:MySQL唯一索引并发插入导致死锁!
|
1月前
|
SQL 关系型数据库 MySQL
案例剖析,MySQL共享锁引发的死锁问题!
案例剖析,MySQL共享锁引发的死锁问题!
|
3月前
|
SQL 关系型数据库 MySQL
遇到mysql数据库死锁,你会怎么排查?
遇到mysql数据库死锁,你会怎么排查?
226 0
|
4天前
|
SQL 算法 关系型数据库
面试:什么是死锁,如何避免或解决死锁;MySQL中的死锁现象,MySQL死锁如何解决
面试:什么是死锁,死锁产生的四个必要条件,如何避免或解决死锁;数据库锁,锁分类,控制事务;MySQL中的死锁现象,MySQL死锁如何解决
|
14天前
|
关系型数据库 MySQL 数据库
一个 MySQL 数据库死锁的案例和解决方案
本文介绍了一个 MySQL 数据库死锁的案例和解决方案。
19 3
|
2月前
|
监控 关系型数据库 MySQL
MySQL锁机制与解决死锁问题
MySQL锁机制与解决死锁问题
242 5
|
27天前
|
监控 关系型数据库 MySQL
一次彻底讲清如何处理mysql 的死锁问题
【10月更文挑战第16天】本文详细介绍了如何处理 MySQL 中的死锁问题,涵盖死锁的概念、原因、检测方法及解决策略,强调通过优化事务设计、调整数据库参数、手动处理和预防措施等手段,有效减少死锁,提升数据库性能与稳定性。
168 0
|
3月前
|
存储 SQL 关系型数据库
深入MySQL锁机制:原理、死锁解决及Java防范技巧
深入MySQL锁机制:原理、死锁解决及Java防范技巧
|
3月前
|
SQL JavaScript 关系型数据库
Mysql索引不当引发死锁问题
本文通过真实案例解析了MySQL在高并发环境下出现死锁的问题。数据库表`t_award`包含多个索引,但在执行特定SQL语句时遭遇索引失效,导致更新操作变慢并引发死锁。分析发现,联合索引`(pool_id, identifier, status, is_redeemed)`因`identifier`允许为空值而导致索引部分失效。此外,`pool_id`上的普通索引产生的间隙锁在高并发下加剧了死锁风险。为解决此问题,文中提出了调整索引顺序至`(pool_id, status, is_redeemed, identifier)`等方案来优化索引使用,进而减轻死锁现象。