MYSQL遇到Deadlock found when trying to get lock,解决方案

最近遇到一个MYSQL update语句出现Deadlock found when trying to get lock的问题,分析一下原因。

什么情况下会出现Deadlock found when trying to get lock?
出现死锁需要2个条件:
1)至少2个client(A,B)同时在执行事务
2)clientA锁定了某一行,未提交事务,此时clientB也需要update/delete这一行,此时clientB就会进入等待状态,直到出现Deadlock 。

如何减少死锁的发生?
很重要的两点,就可以避免这种情况
1)事务操作锁定的行数较少(更精确的索引条件)。
2)保证事务较短的执行时间,完成后马上提交。
这里有更加详细的描述:

例子:
先建立以下一个表,并导入一些数据
CREATE TABLE `test_dead_lock` (
   `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
   `uid` int(11) NOT NULL DEFAULT '0' COMMENT '用户ID',
   `message` varchar(45) NOT NULL DEFAULT '' COMMENT '用户信息',
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='死锁测试'
INSERT INTO `vip_dap`.`test_dead_lock` (`uid`, `message`) VALUES ('1', 'aaa');
INSERT INTO `vip_dap`.`test_dead_lock` (`uid`, `message`) VALUES ('1', 'bbb');
INSERT INTO `vip_dap`.`test_dead_lock` (`uid`, `message`) VALUES ('2', 'ccc');
INSERT INTO `vip_dap`.`test_dead_lock` (`uid`, `message`) VALUES ('2', 'ddd');


clientA执行:
注意,clientA不要COMMIT
START TRANSACTION;
UPDATE test_dead_lock SET message = 'u1' WHERE `uid`=1 LIMIT 1;

clientB执行:
START TRANSACTION;
UPDATE test_dead_lock SET message = 'u2' WHERE `uid`=2 LIMIT 1;
COMMIT;
此时,clientB更新不会成功,虽然按数据来看,他们更新的行是不同的,但是由于uid上没有索引,这个走不了行锁,在uid上添加索引后,clientB就可以执行了。
这里用到的策略就是:事务操作锁定的行数较少
ALTER TABLE `vip_dap`.`test_dead_lock`
ADD INDEX `idx_uid` USING BTREE (`uid` ASC);
<think> 嗯,用户这次问的是MySQL死锁错误的解决方案。从引用内容看,用户已经收集了两条相关参考资料:一个是具体的报错信息,另一个提到了死锁检测的伪代码方案。用户明确表示“上述引用仅供参考”,说明ta需要更全面的解决方案而非简单重复引用内容。 用户可能是个中高级开发者,因为ta能精准定位到数据库死锁这种相对深入的问题。考虑到死锁问题通常出现在高并发场景,用户可能正在处理生产环境的紧急故障,语气里透着焦虑——毕竟这是会阻塞业务流程的严重错误。 关于解决思路,需要分层展开:首先要理解死锁本质(多事务循环等待资源),然后提供三种层级的方案:应急处理、规避策略、深度优化。特别要注意引用中的伪代码给出了“重试机制”的线索,这在实际开发中确实是最常用方案。 技术细节上需要强调几个关键点:innodb_lock_wait_timeout的合理设置、如何降低事务粒度、索引优化的具体方法。还要提醒用户,某些方案如死锁检测虽然有效但可能带来性能损耗。 最后要补充排查工具,因为用户真正需要的是自主解决问题的能力。show engine innodb status的输出解析必须包含,这是诊断死锁的黄金标准。额外注意事项里的事务重试策略特别重要——很多框架其实内置了这类机制,用户可能不知道。 相关问题的设计要覆盖后续可能性:用户解决当前死锁后,可能想预防复发(索引优化)、处理复杂场景(嵌套事务)、或者需要监控方案(性能分析)。这三个衍生方向都很有实战价值。 </think> 以下是解决 MySQL "Deadlock found when trying to get lock" 错误的系统性方案,结合了事务优化、锁机制调整和代码逻辑改进: --- ### **一、核心解决方案** #### 1. **事务重试机制**(最常用) ```java int retryCount = 0; while (retryCount < MAX_RETRY) { try { // 执行事务操作 executeTransaction(); break; // 成功则退出 } catch (DeadlockException e) { retryCount++; Thread.sleep(50 * retryCount); // 指数退避等待 } } ``` > **原理**:死锁通常瞬时发生,重试可使事务重新获取资源锁[^1] #### 2. **降低事务粒度** - **拆分大事务**:将 `UPDATE 10,000 rows` 拆分为多个小事务(如每100行一次提交) - **调整执行顺序**:确保所有事务按**相同顺序**访问资源(如先更新表A再更新表B) - **避免热点更新**:高并发时减少对同一行的频繁更新(如计数器改用 `SELECT ... FOR UPDATE`) #### 3. **锁优化策略** | 策略 | 操作方法 | |---------------------|--------------------------------------------------------------------------| | **缩短锁持有时间** | 将非DB操作(业务逻辑)移出事务范围 | | **降低隔离级别** | `SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;` | | **索引优化** | 确保WHERE条件列均有索引,避免全表扫描(`EXPLAIN` 分析查询) | | **使用行锁提示** | `SELECT ... FOR UPDATE NOWAIT` / `SELECT ... LOCK IN SHARE MODE SKIP LOCKED` | --- ### **二、高级调优方案** #### 1. **死锁检测与诊断** ```sql SHOW ENGINE INNODB STATUS; -- 查看最新死锁记录 ``` 关注输出中的 `LATEST DETECTED DEADLOCK` 部分,明确: - 冲突的SQL语句 - 被阻塞的资源(行/索引) - 事务等待链 #### 2. **参数调优** ```ini # my.cnf 配置 innodb_lock_wait_timeout=30 # 缩短锁超时(默认50秒) innodb_deadlock_detect=ON # 确保死锁检测开启(默认ON) innodb_print_all_deadlocks=ON # 记录所有死锁到错误日志 ``` #### 3. **应用层设计** - **乐观锁机制**:使用版本号控制更新 ```sql UPDATE table SET col=new_val, version=version+1 WHERE id=123 AND version=current_version; ``` - **队列处理**:对同一资源的操作串行化(如Redis队列) --- ### **三、临时应急措施** ```sql -- 终止阻塞进程(需SUPER权限) SHOW PROCESSLIST; KILL [blocking_thread_id]; ``` --- ### **排查流程图** ```mermaid graph TD A[出现死锁错误] --> B{分析 SHOW ENGINE INNODB STATUS} B --> C[定位冲突SQL及资源] C --> D[优化SQL/索引] D --> E[缩短事务粒度] E --> F[引入重试机制] F --> G[测试验证] G --> H{是否解决?} H -->|否| I[考虑队列/乐观锁] ``` > **关键预防点**:事务中避免**交叉申请锁**(如事务1锁A后锁B,事务2锁B后锁A)[^2] --- **相关问题** 1. 如何通过 `SHOW ENGINE INNODB STATUS` 解析MySQL死锁日志? 2. 在高并发场景下,如何设计数据库操作避免死锁? 3. MySQL的锁升级机制在什么情况下会触发?如何规避? [^1]: 重试机制是应对瞬时死锁的有效方案,需配合退避算法 [^2]: 规范资源访问顺序可破坏死锁的循环等待条件
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值