MySQL 死锁处理机制
一、死锁检测
-
自动检测:
- MySQL(特别是InnoDB引擎)会自动检测死锁
- 使用等待图算法分析事务间的锁依赖关系
- 当检测到循环等待(A等B,B等A)时判定为死锁
-
检测频率:
- InnoDB默认每5秒检测一次死锁
- 发生死锁时会立即中断其中一个事务
二、死锁处理方式
-
自动回滚:
- MySQL会自动选择一个事务进行回滚
- 通常选择代价最小的事务回滚(如修改行数少的事务)
- 回滚后释放所有锁资源
-
错误提示:
- 被回滚的事务会收到错误码
1213
(Deadlock found) - 应用程序需要捕获该错误并重试事务
- 被回滚的事务会收到错误码
三、开发者应对策略
- 代码层处理:
-- 典型处理流程 BEGIN; -- 业务操作 COMMIT;
- 捕获死锁错误后重试:
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
public class DeadlockRetryExample {
// 最大重试次数
private static final int MAX_RETRIES = 3;
public void executeWithRetry(Connection connection, String sql) throws SQLException {
int retryCount = 0;
while (retryCount < MAX_RETRIES) {
try {
// 执行SQL操作
try (Statement statement = connection.createStatement()) {
statement.executeUpdate(sql);
}
// 如果执行成功,直接返回
return;
} catch (SQLException e) {
// 检查是否是死锁错误(MySQL错误码1213)
if (isDeadlockError(e)) {
retryCount++;
if (retryCount >= MAX_RETRIES) {
// 达到最大重试次数,抛出异常
throw new SQLException("操作失败,已达到最大重试次数", e);
}
// 等待一段时间再重试(可自定义等待时间)
try {
Thread.sleep(100 * retryCount); // 每次重试等待时间递增
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new SQLException("操作被中断", ie);
}
} else {
// 如果不是死锁错误,直接抛出
throw e;
}
}
}
}
// 判断是否是死锁错误
private boolean isDeadlockError(SQLException e) {
// MySQL死锁错误码是1213
return e.getErrorCode() == 1213 ||
"Deadlock found when trying to get lock".equals(e.getMessage());
}
// 使用示例
public static void main(String[] args) {
// 这里需要提供实际的数据库连接
// Connection connection = ...;
DeadlockRetryExample example = new DeadlockRetryExample();
try {
// 示例SQL,实际使用时替换为你的SQL
String sql = "UPDATE your_table SET column1 = 'value' WHERE id = 1";
example.executeWithRetry(connection, sql);
} catch (SQLException e) {
System.err.println("操作最终失败: " + e.getMessage());
}
}
}
-
优化事务设计:
- 缩短事务时长:减少事务持有锁的时间
- 按固定顺序访问表/行:避免交叉访问导致的死锁
- 减少事务粒度:将大事务拆分为多个小事务
- 避免长事务:特别是包含用户交互的事务
-
监控与分析:
- 使用
SHOW ENGINE INNODB STATUS
查看最近死锁详情 - 分析死锁日志找出常见模式
- 使用性能监控工具(如Percona Toolkit)跟踪锁等待
- 使用
四、预防措施
-
应用层设计:
- 实现幂等操作,允许安全重试
- 对关键业务增加重试逻辑
-
数据库配置:
- 调整
innodb_deadlock_detect
(MySQL 8.0+可关闭检测,但一般不建议) - 优化隔离级别(根据业务选择RC或RR)
- 调整
-
架构优化:
- 减少跨表事务
- 使用乐观锁替代悲观锁(适合冲突少的场景)
五、关键注意事项
- 不要忽略死锁错误:
- 必须处理
1213
错误,否则业务逻辑会中断
- 必须处理
- 不要过度依赖自动重试:
- 高并发下频繁死锁可能是设计问题
- 生产环境监控:
- 定期检查死锁日志,分析根本原因
MySQL的死锁处理是自动化的,但开发者仍需通过优化事务设计和实现重试机制来应对死锁问题,确保系统稳定运行。