Error 1205应优先判断事务是否真需重试,因MySQL已选择代价小的事务回滚;仅在幂等、可安全重试场景下限次重试,并须通过错误码精准捕获而非字符串匹配。

MySQL死锁报错Error 1205到底该不该重试
遇到Error 1205(Deadlock found when trying to get lock),第一反应不是“赶紧捕获重试”,而是先确认:这个事务是不是真的该被回滚。MySQL主动杀掉的是**代价更小的那个事务**,它已经帮你做了选择——强行重试反而可能放大冲突。
常见错误现象:ERROR 1205 (40001): Deadlock found when trying to get lock 突然出现,但业务日志里没明显并发写同一行;或者重试后又立刻报一次Error 1205,形成循环。
- 只在明确知道事务可安全重试时才加重试逻辑(比如幂等更新用户积分、扣减库存且已校验余额)
- 不要对INSERT ... SELECT、UPDATE多表关联、或含子查询的事务盲目重试——这类语句锁范围难预估,重试大概率再撞上
- 应用层重试建议限制1–2次,间隔随机化(如50ms–200ms),避免所有客户端同步重试造成雪崩
如何快速定位哪两条SQL在互相锁住
Error 1205日志本身不直接告诉你谁锁了谁,但MySQL会在错误日志里附上最近的死锁信息(需开启innodb_print_all_deadlocks=ON)。关键看WAITING FOR THIS LOCK TO BE GRANTED和HOLDS THE LOCK(S)两段。
使用场景:线上突发大量Error 1205,要立刻判断是业务逻辑缺陷,还是索引缺失导致锁升级。
- 检查死锁日志中涉及的表是否缺少合适索引——例如
UPDATE user SET status=1 WHERE name='xxx'没走索引,会锁全表,极易引发死锁 - 注意
REPLACE INTO、INSERT ... ON DUPLICATE KEY UPDATE在唯一键冲突时会加next-key lock,和普通UPDATE行为不同 - 用
SELECT * FROM performance_schema.data_locks(MySQL 8.0+)实时查当前锁,但别在高峰期频繁查,开销不小
为什么加了索引还是死锁:UPDATE顺序很关键
即使每条UPDATE都走了索引,只要两个事务以不同顺序更新同一组行,照样死锁。比如事务A先改id=1再改id=2,事务B反过来,就构成经典环形等待。
参数差异:InnoDB的锁是行级,但实际加锁行为受隔离级别影响——READ COMMITTED下只锁匹配到的行,REPEATABLE READ下还可能锁间隙(gap lock),扩大冲突面。
- 业务代码中,对一批主键更新,务必统一按
ORDER BY id ASC排序后再执行,避免顺序混乱 - 批量操作尽量拆成单条,或用
INSERT ... ON DUPLICATE KEY UPDATE替代先查后更,减少锁持有时间 - 避免在事务里做HTTP调用、文件读写等长耗时操作——锁持有时间越长,死锁概率指数上升
捕获Error 1205的正确姿势(PHP/Python示例)
不是所有ORM都原生支持区分死锁和其它错误。硬靠except Exception捕获再字符串匹配"1205"太脆弱;用数据库驱动提供的错误码判断才可靠。
性能影响:每次连接都做错误码解析无额外开销,但若在重试逻辑里没控制次数,可能把瞬时压力变成持续压测。
- PHP PDO:
if ($e->getCode() === '40001' && strpos($e->getMessage(), '1205') !== false) - Python PyMySQL:
if exc.errno == 1205(注意不是sqlstate) - Java JDBC:
SQLException.getSQLState().equals("40001") && e.getErrorCode() == 1205
真正容易被忽略的点:很多框架的“事务模板”默认把Error 1205当普通异常吞掉并回滚,但没暴露出来——你得去翻它底层的异常映射表,确认1205有没有被误转成DataIntegrityViolationException之类泛化异常。


















