MySQL相关 - 死锁的发生和避免

本文深入探讨了数据库中锁的使用与管理,包括锁的释放条件、死锁的产生及检测方法,并提供了避免死锁的策略。通过具体示例说明了如何分析事务与锁的情况,以及如何解决由锁引发的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在我们使用锁的时候,有一个问题是需要注意和避免的,我们知道,排它锁有互斥的特性。一个事务或者说一个线程持有锁的时候,会阻止其他的线程获取锁,这个时候会造成阻塞等待,如果循环等待,会有可能造成死锁

这个问题我们需要从几个方面来分析,一个是锁为什么不释放,第二个是被阻塞了怎么办,第三个死锁是怎么发生的,怎么避免。我们且看正文部分。

: 在这里插入图片描述

正文

死锁

锁的释放与阻塞

回顾:锁什么时候释放?

事务结束(commit,rollback);客户端连接断开。

如果一个事务一直未释放锁,其他事务会被阻塞多久?会不会永远等待下去?如果是,在并发访问比较高的情况下,如果大量事务因无法立即获得所需的锁而挂起,会占用大量计算机资源,造成严重性能问题,甚至拖跨数据库。

[Err] 1205 - Lock wait timeout exceeded; try restarting transaction MySQL 有一个参数来控制获取锁的等待时间,默认是 50 秒。

show VARIABLES like 'innodb_lock_wait_timeout';

在这里插入图片描述

对于死锁,是无论等多久都不能获取到锁的,这种情况,也需要等待 50 秒钟吗?那

不是白白浪费了 50 秒钟的时间吗?

我们先来看一下什么时候会发生死锁。

死锁的发生和检测

死锁演示:

Session 1Session 2
begin;<br/>select * from t2 where id =1 for update; 
-begin;<br />delete from t2 where id =4 ;
update t2 set name= '4d' where id =4 ; 
-delete from t2 where id =1 ;

在第一个事务中,检测到了死锁,马上退出了,第二个事务获得了锁,不需要等待 50 秒:

[Err] 1213 - Deadlock found when trying to get lock; try restarting transaction

为什么可以直接检测到呢?是因为死锁的发生需要满足一定的条件,所以在发生死锁时,InnoDB 一般都能通过算法(wait-for graph)自动检测到。

那么死锁需要满足什么条件?死锁的产生条件:

因为锁本身是互斥的 (1)同一时刻只能有一个事务持有这把锁; (2)其他的事务需要在这个事务释放锁之后才能获取锁,而不可以强行剥夺; (3)当多个事务形成等待环路的时候,即发生死锁。

举例:

理发店有两个总监。一个负责剪头的 Tony 总监,一个负责洗头的 Kelvin 总监。

Tony 不能同时给两个人剪头,这个就叫互斥。

Tony 在给别人在剪头的时候,你不能让他停下来帮你剪头,这个叫不能强行剥夺。如果 Tony 的客户对 Kelvin 总监说:你不帮我洗头我怎么剪头?Kelvin 的客户对 Tony 总监说:你不帮我剪头我怎么洗头?这个就叫形成等待环路。

如果锁一直没有释放,就有可能造成大量阻塞或者发生死锁,造成系统吞吐量下降,这时候就要查看是哪些事务持有了锁。

查看锁信息(日志)

SHOW STATUS 命令中,包括了一些行锁的信息:

show status like 'innodb_row_lock_%';

在这里插入图片描述

  • Innodb_row_lock_current_waits:当前正在等待锁定的数量;
  • Innodb_row_lock_time :从系统启动到现在锁定的总时间长度,单位 ms;
  • Innodb_row_lock_time_avg :每次等待所花平均时间;
  • Innodb_row_lock_time_max:从系统启动到现在等待最长的一次所花的时间;
  • Innodb_row_lock_waits :从系统启动到现在总共等待的次数。

SHOW 命令是一个概要信息。InnoDB 还提供了三张表来分析事务与锁的情况:

select * from information_schema.INNODB_TRX;	-- 当前运行的所有事务 ,还有具体的语句

在这里插入图片描述

select * from information_schema.INNODB_LOCKS;	--  当前出现的锁

在这里插入图片描述

select * from information_schema.INNODB_LOCK_WAITS;	--  锁等待的对应关系

在这里插入图片描述

找出持有锁的事务之后呢?

如果一个事务长时间持有锁不释放,可以 kill 事务对应的线程 ID ,也就是 INNODB_TRX 表中的 trx_mysql_thread_id,例如执行 kill 4,kill 7,kill 8。

当然,死锁的问题不能每次都靠 kill 线程来解决,这是治标不治本的行为。我们应该尽量在应用端,也就是在编码的过程中避免。

有哪些可以避免死锁的方法呢?

死锁的避免

  1. 在程序中,操作多张表时,尽量以相同的顺序来访问(避免形成等待环路);
  2. 批量操作单张表数据的时候,先对数据进行排序(避免形成等待环路);
  3. 申请足够级别的锁,如果要操作数据,就申请排它锁;
  4. 尽量使用索引访问数据,避免没有 where 条件的操作,避免锁表;
  5. 如果可以,大事务化成小事务;
  6. 使用等值查询而不是范围查询查询数据,命中记录,避免间隙锁对并发的影响。
<think>好的,我现在需要帮助用户解决MySQL中的死锁问题。首先,我得回顾一下用户提供的引用内容,看看里面提到了哪些关键点。用户引用了五篇资料,主要涉及死锁的原因、检测工具、解决方法以及监控参数设置。 首先,用户的问题是如何诊断解决MySQL中的死锁。根据引用内容,死锁的产生通常与事务锁的竞争有关,比如事务处理不当或索引缺失。诊断部分提到了错误日志、SHOW ENGINE INNODB STATUS命令,以及开启innodb_print_all_deadlocks参数来记录所有死锁信息。解决方法包括设置锁等待超时、优化事务SQL、使用索引、调整隔离级别等。 接下来,我需要组织这些信息,确保结构清晰。可能分为诊断步骤解决步骤两部分。诊断部分要详细说明如何获取死锁信息,比如查看错误日志,使用命令获取死锁详情。解决部分需要列出不同的策略,并给出具体例子,比如调整事务大小,添加索引,设置超时时间等。 还要注意用户提到的系统级指令,比如使用正确的中文回答,生成相关问题,引用格式正确。需要确保回答中的技术术语准确,比如正确使用InnoDB引擎的相关命令。此外,生成的相关问题要基于引用内容,可能涉及死锁监控、参数配置、避免死锁的最佳实践等。 需要检查是否有遗漏的关键点,比如引用5提到锁的类型冲突,可能需要提到锁的类型对死锁的影响。此外,引用3提到了监控数据库运行状态,这可能涉及到预防死锁的措施,比如性能监控查询优化。 最后,要确保回答中的引用标注正确,每个解决方法的步骤都有对应的引用来源。例如,提到使用SHOW ENGINE INNODB STATUS时,引用2引用4都有相关说明。设置innodb_lock_wait_timeout参数来自引用5吗?需要核对每个建议的来源是否正确标注。 可能还需要考虑用户的实际操作场景,比如对于不熟悉MySQL命令的管理员,如何一步步指导他们查看日志或执行命令。同时,避免过于技术化的术语,保持解释的易懂性,但又要准确。 总结一下,回答的结构应该是:首先解释诊断步骤,包括查看错误日志、使用特定命令、开启参数;然后详细说明解决方法,分点列出各种策略,每个策略给出具体操作引用来源;最后生成相关问题。需要确保所有引用都正确对应,并且建议的方法有据可依。</think>### MySQL死锁诊断与解决方法 #### 一、诊断死锁问题 1. **查看MySQL错误日志** MySQL错误日志默认记录死锁事件的详细信息,包括时间、涉及的事务锁资源[^2][^4]。 日志路径可通过以下命令查询: ```sql SHOW VARIABLES LIKE 'log_error'; ``` 2. **使用`SHOW ENGINE INNODB STATUS`** 执行该命令可获取最新的死锁信息,包含**死锁图**、事务ID、持有/等待的锁类型及具体SQL语句[^3]。 ```sql SHOW ENGINE INNODB STATUS\G ``` 3. **启用`innodb_print_all_deadlocks`参数** 通过全局设置将此参数设为`ON`,所有死锁事件将自动记录到错误日志中[^4]: ```sql SET GLOBAL innodb_print_all_deadlocks = ON; ``` #### 二、解决死锁的常用方法 1. **优化事务与SQL设计** - **缩短事务时间**:减少事务操作步骤,避免长时间持有锁[^1][^3]。 - **统一操作顺序**:确保不同事务访问相同资源的顺序一致,减少循环等待[^5]。 - **添加合理索引**:通过索引减少全表扫描,降低锁冲突概率。 2. **调整锁等待超时参数** 设置`innodb_lock_wait_timeout`(默认50秒),强制超时回滚事务以解除死锁: ```sql SET GLOBAL innodb_lock_wait_timeout = 30; ``` 3. **降低隔离级别** 将事务隔离级别从`REPEATABLE READ`改为`READ COMMITTED`,减少间隙锁(Gap Lock)的使用: ```sql SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; ``` 4. **死锁自动回滚机制** InnoDB引擎默认启用死锁检测,自动选择代价小的事务回滚。可通过`innodb_deadlock_detect`控制此行为。 5. **代码层重试机制** 在应用程序中捕获死锁错误(错误码`1213`),自动重试事务[^1]: ```python try: with transaction.atomic(): # 执行数据库操作 except DatabaseError as e: if 'Deadlock found' in str(e): retry_transaction() # 自定义重试逻辑 ``` #### 三、预防死锁的运维实践 - **监控锁竞争**:定期检查`information_schema.INNODB_TRX``INNODB_LOCKS`表[^3]。 - **分析慢查询**:优化执行时间过长的SQL,减少锁持有时间。 - **压力测试**:在高并发场景下模拟业务流量,提前发现潜在死锁---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值