应从预防、捕获、重试、降级四层面系统应对Spring Boot并发死锁:识别MySQL错误码1213或40001,显式捕获并隔离死锁异常;用@Retryable实现幂等重试,配置指数退避;按固定顺序访问表、缩短事务范围、用原子更新减少死锁;通过MySQL日志与应用监控及时告警降级。

Spring Boot 中遇到并发死锁异常,不能只靠“重试”硬扛,得从预防、捕获、重试、降级四个层面系统应对。MySQL 报出 Deadlock found when trying to get lock 时,说明事务之间已形成循环等待,数据库已主动回滚其中一个事务——此时应用必须能识别它、不抛错、不重复执行、并合理重试。
识别并隔离死锁异常
Spring 的 @Transactional 默认在遇到 SQLException 且 SQL 状态码为 40001(deadlock)或 41000(serialization failure)时不会自动重试。你需要显式捕获:
- 用
@ExceptionHandler拦截TransactionSystemException或其包裹的LockAcquisitionException - 检查底层异常是否含 “deadlock” 字样或匹配 MySQL 错误码
1213 - 避免把所有数据库异常都当成死锁处理(比如唯一键冲突、连接超时等需区别对待)
用 @Retryable 实现幂等重试
引入 spring-retry 后,给业务方法加上 @Retryable 注解是最轻量的方案,但必须满足幂等前提:
- 所有写操作带唯一业务标识(如
"PAY_" + orderId),确保重复执行不产生副作用 - 读操作使用
SELECT ... FOR UPDATE或加@Transactional(isolation = Isolation.REPEATABLE_READ)避免幻读干扰判断 - 配置指数退避:初始延迟 200ms,倍数 1.8,最大延迟 3 秒(金融类建议 ≤2 秒)
从源头减少死锁概率
重试只是兜底,治本要优化事务行为:
- 按固定顺序访问多张表(如总是先更新
order再更新inventory),打破循环等待链 - 缩短事务范围:把非 DB 操作(如发消息、调远程接口)移出
@Transactional方法体 - 避免在事务中执行长耗时逻辑(如大文件解析、复杂计算),防止锁持有时间过长
- 对高频更新字段(如库存 count)考虑用原子更新语句:
UPDATE item SET stock = stock - 1 WHERE id = ? AND stock >= 1
监控与快速止损
死锁不是偶发故障,而是系统压力的信号:
- 开启 MySQL 的
innodb_print_all_deadlocks = ON,定期采集死锁日志分析热点行和事务模式 - 在应用层记录死锁重试次数,当单分钟内重试超 5 次,触发告警并临时降级(如切到缓存库存校验)
- 配合 Druid 或 HikariCP 监控连接池活跃数、SQL 平均执行时长,及时发现慢查询引发的锁堆积


















