应采用带随机扰动的指数退避重试:首次失败后等待10ms,后续每次加倍并叠加±10%随机抖动,最多重试3次,超限则抛异常;需精准捕获错误码1213,避免误重试其他异常。

死锁报错 ERROR 1213 (40001): Deadlock found when trying to get lock 怎么重试才不雪崩
直接重试一次大概率再撞上同一个死锁——因为事务排队顺序没变,资源争抢模式照旧。必须让重试时间“错开”,否则并发越高,重试越集中,死锁反而更频繁。
核心是用指数退避:第1次失败后等 10ms,第2次等 20ms,第3次等 40ms……上限建议设在 500ms 以内。别用固定间隔(比如每次都等 100ms),那等于给所有失败事务发同步闹钟。
- 退避基数选
10ms是因 MySQL 行锁释放通常在毫秒级,太短(如1ms)意义不大;太长(如100ms)会拖慢平均响应 - 最大重试次数建议 ≤
3次,再不成功就该抛异常,而不是硬扛——说明业务逻辑本身存在结构性争抢(比如总按固定顺序更新user和order表) - 退避时间要加随机扰动(比如 ±10%),避免多个客户端在同一时刻重试,典型做法:
sleep(rand() * base_delay * 0.2 + base_delay)
Python 用 mysql-connector-python 实现带退避的事务重试
不要自己写 while 循环套 try/except 然后手动 sleep——容易漏掉连接断开、超时等非死锁错误,也难控制重试上下文。
推荐封装一个最小可用的重试函数,只捕获明确的死锁错误码:
def execute_with_deadlock_retry(cursor, query, params=None, max_retries=3):
base_delay = 0.01 # 10ms
for i in range(max_retries + 1):
try:
cursor.execute(query, params)
return
except mysql.connector.Error as e:
if e.errno == 1213 and i
-
cursor必须是同一事务内的,不能每次重试都新开连接或新start_transaction(),否则隔离性失效 - 别把
autocommit=True的连接塞进来——这种连接没法回滚,重试也没意义 - 如果 query 含多个语句(比如用分号连写),MySQL 可能只返回第一个语句的错误码,导致死锁被忽略,务必拆成单语句执行
Go 里用 database/sql 配合 sql.ErrNoRows 类思路处理死锁
Go 标准库不直接暴露 MySQL 错误码,得靠 errors.As 提取底层驱动错误。MySQL 驱动(go-sql-driver/mysql)会把死锁包装成 *mysql.MySQLError,其中 Number 字段为 1213。
- 别用
err.Error()字符串匹配——版本升级后提示可能变,比如从"Deadlock found"改成"Transaction deadlock" - 重试时用
context.WithTimeout控制总耗时,防止某次退避后卡住(比如系统负载突增导致 sleep 偏差大) - 如果事务里调用了外部 HTTP 服务,重试前必须确保幂等——否则一次死锁重试可能触发两次扣款
为什么不能依赖 MySQL 的 innodb_lock_wait_timeout 自动中断
这个参数只管“等锁等多久”,不是“死锁检测开关”。死锁由 InnoDB 主动检测并干掉其中一个事务,跟这个 timeout 完全无关。设再大也没用,该死锁还是秒报错。
真正影响它的是另一组参数:innodb_deadlock_detect(默认 on,关了就靠 timeout 被动发现,但代价是可能卡住几秒)和 innodb_print_all_deadlocks(调试用,别在线上常开)。
- 线上环境严禁关闭
innodb_deadlock_detect——虽然能降低 CPU 开销,但会让死锁变成“不可预测的锁等待超时”,排查难度陡增 -
innodb_lock_wait_timeout建议保持默认50秒,改小对死锁无感,只会影响普通行锁等待场景 - 死锁日志里关键要看
*** (1) TRANSACTION:和*** (2) TRANSACTION:下面各自持有的锁和等待的锁,这才是定位争抢路径的唯一依据
实际最难的从来不是加几行退避代码,而是确认哪几个 SQL 真正在抢同一组资源——这得靠 SHOW ENGINE INNODB STATUS 里那段被截断的死锁日志,以及业务代码里对表/索引访问顺序的一致性约束。漏掉这点,重试只是把问题从“立刻失败”延后成“稍后失败”。











