首页/数据/数据库速查/应用侧锁错误处理(1213/1205 重试)
性能与调优57 条

应用侧锁错误处理(1213/1205 重试)

数据库锁错误要有明确应用策略:1213 死锁与 1205 超时均可安全重试(事务边界内重做),用有界退避+幂等保证正确性。

数据库速查数据更新于 2026-10-13

应用侧锁错误处理是什么

数据库的锁错误在应用层必须有明确策略:1213(死锁,事务被回滚)与 1205(锁等待超时)都属于「重试就可能成功」的错误,前提是整个事务可重放且业务幂等;而 1062(唯一键冲突)这类语义错误重试没有意义。做法是把重试放在事务边界外,带上有界次数与随机退避,超过上限就告警并抛出,避免风暴期把数据库压垮。

基本信息

可重试错误1213 死锁被回滚、1205 锁等待超时
不可重试1062 唯一键冲突、1146 表不存在等语义错误
重试边界整个事务从 BEGIN 开始重做,不能只重试单条语句
重试策略有界次数(如 3 次)+ 指数退避 + 随机抖动
前置条件事务内操作幂等或可安全重放
相关参数innodb_lock_wait_timeout 控制等待上限

典型使用场景

  • 秒杀或抢单场景:热点行并发更新频繁产生死锁,需要自动重试吸收
  • 批量任务并发写同一批记录:偶发锁超时不应直接失败
  • 订单状态流转:多表更新顺序不一致时死锁概率上升
  • 数据同步与补偿任务:重试是常态,需保证重复执行结果一致
  • 故障降级评估:重试上限与告警阈值决定系统在锁风暴中的行为

重试伪代码

重试伪代码
for attempt in 1..3:
  try:
    BEGIN
    ...业务写入...
    COMMIT
    break
  except Deadlock(1213) or LockWaitTimeout(1205):
    ROLLBACK
    sleep(50ms * attempt + random(0..30ms))   # 退避加抖动,避免同步重试
  except OtherError as e:
    raise e            # 语义错误不重试,直接上报
# 超过次数:记录上下文并告警,避免无限重试

确认加锁范围与超时设置

确认加锁范围与超时设置
-- 事务内实际锁了多少行,行数大说明条件没走索引
SELECT trx_id, trx_state, trx_rows_locked, trx_rows_modified, LEFT(trx_query, 80)
FROM information_schema.INNODB_TRX ORDER BY trx_rows_locked DESC;

-- 单会话放宽等待时间(批处理场景)
SET SESSION innodb_lock_wait_timeout = 120;

-- 查最近一次死锁涉及的事务与语句
SHOW ENGINE INNODB STATUS\G   # 只看 LATEST DETECTED DEADLOCK 段

错误码与处置

错误码含义处置方式
1213死锁,InnoDB 回滚了事务整事务重试,通常一次即可成功
1205锁等待超时重试或延长等待时间,同时排查长事务
1062唯一键冲突业务语义问题,不重试,按业务规则处理
1206锁表数量上限事务过大,需拆分批次
3572并发 DDL 与 DML 冲突错峰执行结构变更
退避要加随机抖动:固定间隔重试会让所有失败请求在同一时刻再次冲击同一行,把瞬时冲突变成持续冲突;有界重试配合告警,才能在风暴期保留人工介入的入口。
只有在事务内所有操作可安全重放(幂等)时才允许自动重试:如果事务里包含扣减库存、发送消息等不可回滚的外部副作用,重试会造成重复扣减或重复通知。

易错点

  • 在非事务上下文重试单条 UPDATE:部分成功的语义不清,必须整事务重做
  • 重试没有次数上限:死锁风暴中被持续放大,数据库越忙越乱
  • 重试期间不加退避:所有请求同时重试,冲突概率不降反升
  • 把不可重试错误一起重试:唯一键冲突重试无意义,只是白白增加负载与延迟
  • 忽略锁等待的根本原因:事务里塞入远程调用或用户交互,持有时间被拉长,重试只是掩盖症状

数据来源与参考

  • MySQL 8.0 Reference Manual: Deadlocks
  • MySQL 8.0 Reference Manual: Error 1213

相关工具

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌