Java处理数据库死锁需精准识别异常(MySQL:1213/40001,PostgreSQL:40P01,SQL Server:1205,Oracle:60),配合指数退避(如50ms→100ms→200ms)与3–5次重试上限,每次重试前刷新业务数据防重复。

Java 中处理数据库死锁异常并自动重试,核心是捕获特定的 SQL 异常(如 MySQL 的 1213、PostgreSQL 的 40P01、SQL Server 的 1205),识别后进行有限次数的延迟重试。关键在于不盲目重试,而要精准识别死锁、控制重试逻辑、避免雪崩。
识别数据库死锁异常
不同数据库抛出的死锁异常类型和错误码不同,需按实际使用的数据库适配:
-
MySQL:通常抛出
SQLException,getSQLState()为"40001"或getErrorCode()为1213 -
PostgreSQL:
getSQLState()为"40P01" -
SQL Server:
getErrorCode()为1205 -
Oracle:
getErrorCode()为60
推荐封装一个工具方法判断是否为死锁异常,例如:
(示例:Spring JDBC 场景)private static boolean isDeadlockException(SQLException ex) {
String sqlState = ex.getSQLState();
int errorCode = ex.getErrorCode();
return "40P01".equals(sqlState) // PostgreSQL
|| "40001".equals(sqlState) // MySQL / Standard SQL
|| errorCode == 1213 || errorCode == 1205 || errorCode == 60;
}
实现带退避策略的重试逻辑
简单 for 循环重试容易打满数据库,应加入指数退避(Exponential Backoff)和最大重试次数限制:
- 初始延迟建议 10–100ms,每次重试翻倍(如 10ms → 20ms → 40ms)
- 最多重试 3–5 次(再高成功率极低,且可能掩盖设计问题)
- 每次重试前重新生成业务数据(如订单号、时间戳),避免重复提交相同请求
示例(无框架纯 JDBC):
@Retryable
- 确保重试发生在新事务中(即方法需有
@Transactional,且@Retryable方法不能是 private/protected) - 配置只对死锁异常重试,并指定退避策略
- 注意:Spring 默认事务传播行为是
REQUIRED,重试时需确保上层事务已回滚,推荐将重试方法拆到独立 service bean 中
@Service
public class OrderService {
<pre class="brush:php;toolbar:false;">@Retryable(
value = { SQLException.class },
include = { SQLException.class },
maxAttempts = 3,
backoff = @Backoff(delay = 50, multiplier = 2)
)
@Transactional
public void placeOrder(Order order) throws SQLException {
if (isDeadlockProneOperation()) {
jdbcTemplate.update("UPDATE inventory SET stock = stock - ? WHERE sku = ?",
order.getQuantity(), order.getSku());
}
}
@Recover
public void recover(SQLException ex, Order order) {
log.error("订单[{}]放置失败,已重试3次仍死锁", order.getId(), ex);
throw new BusinessException("库存扣减频繁冲突,请稍后重试");
}}
根本性规避比重试更重要
重试只是兜底手段,高频死锁说明存在设计隐患,应同步优化:
- 统一 DML 操作顺序(如所有事务先更新用户表、再更新订单表),避免循环等待
- 减少事务粒度:把长事务拆成多个短事务,避免锁持有时间过长
- 必要时用
SELECT ... FOR UPDATE SKIP LOCKED(MySQL 8.0+/PostgreSQL)跳过已被锁的行 - 对热点数据(如账户余额)考虑分段锁、版本号乐观锁或异步扣减+补偿
死锁不是“偶尔发生”,而是并发路径暴露了资源竞争缺陷。代码重试能提升可用性,但架构优化才能真正根治。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











