Deadlock found when trying to get lock(死锁)问题解决

在高并发环境下,登录接口出现大量死锁问题,导致错误率升高。通过日志发现更新用户登录时间和插入用户日志时存在锁等待。初步认为是幻读引起,调整事务隔离级别为SERIALIZABLE无效。进一步排查发现,持久态对象导致级联更新,引发死锁。即使去除对象依赖和外键,问题仍未解决。最终,发现外键约束在插入userLog时锁定user数据,导致更新操作死锁。移除外键后,成功解决死锁问题。

今天压测用户中心的登录接口。发现这个接口在并发下几乎不可用,错误率70%。

查看后台日志全是:

登录接口出现死锁了。

看了下代码,登录中做了更新用户登录时间,插入用户log。查询数据库,发行用户更新和插入log都会lock wait

判断可能是在压测同一个用户登录时,某个请求中的事务读取到了另一个请求里事务未提交的数据。从而需要等待之前的事务提交。(幻读)

然后将事务隔离机制改成SERIALIZABLE。

@Transactional(isolation= Isolation.SERIALIZABLE)。

然而并不能解决问题。

后来发现问题出现在记录用户日志上

save(user)后,user对象是持久态,然后把持久态的user塞到userlog对象里

<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]: 规范资源访问顺序可破坏死锁的循环等待条件
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山间明月江上清风_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值