因为专注所以专业
助力成长与创新,汇集前沿程序开发观点

库存和订单同时更新,偶尔报 Deadlock found,只把重试次数调大能压住吗?

2026年9月24日 阅读:30

数据库偶发 Deadlock found 时,只把重试次数调大,通常只能把报错压住一段时间,不能保证根因被修掉。按 2026 年常见交付经验,死锁更常见的诱因是事务持锁范围、索引用法或多入口访问顺序不一致,重试只是可重放业务的一道兜底。先拿到死锁日志还原加锁路径,再决定是统一顺序、缩小事务、补索引减少锁范围,还是叠加幂等重试。

死锁到底卡在哪,为什么只换语句顺序不够

死锁是两个或多个事务各自持有部分行锁,又互相等待对方释放,形成循环等待,数据库检测到后主动回滚其中一个事务。它和普通锁等待的区别是:锁等待只要持有方提交或回滚就能继续,死锁不主动干预就会互相卡住。因此只盯 SQL 表面顺序,容易漏掉索引缺失导致锁范围扩大、事务里夹了远程调用等更常见的诱因。

  • 循环等待:事务 A 持有行 1 等行 2,事务 B 持有行 2 等行 1,两边都不释放。
  • 锁范围被放大:where 条件没走索引,行锁可能升级为更大范围的锁,碰撞概率上升。
  • 事务过长:事务里做远程调用、批量操作、文件读写,持锁时间被拉长。
  • 隔离级别影响:可重复读下间隙锁会额外锁住范围,不同数据库表现有差异。
  • 多入口并发:在线请求、定时任务、消息消费同时更新同一批行,访问顺序很难天然一致。

在实际系统里,死锁往往不是单一原因,而是「无索引 + 事务长 + 多入口」叠加。只换两条语句的顺序,可能刚好覆盖一种固定反向访问的场景,换个入口或数据分布就复现。

先看哪些证据,再决定怎么改

排查死锁不要先改代码,先收集证据。数据库一般会在错误日志或死锁日志里输出两个事务的 SQL、持有锁和等待锁信息。把同一时间段的日志按事务 ID 对齐,才能还原谁先锁了哪一行。若拿不到日志,再按应用日志时间戳、慢查询和事务边界交叉定位,具体日志字段可按所用数据库官方文档核对。

  1. 打开死锁日志:确认数据库是否输出最近一次死锁详情,常见做法是保留最近若干次,便于复现时比对。
  2. 还原加锁顺序:把两个事务的 SQL 按执行时间排列,标出各自持有的行和等待的行。
  3. 核对索引命中:用执行计划看 where 条件是否走索引,没走索引会让锁范围超出预期。
  4. 检查事务边界:确认事务里是否包含远程调用、消息发送、批量循环等拉长持锁时间的操作。
  5. 记录复现条件:把并发入口、数据量级、隔离级别、锁等待超时记下来,避免改完靠感觉判断。

这五步的顺序不能反。先拿日志,再判断顺序,最后才谈改法。直接凭印象换 SQL 顺序,可能在低并发测试环境看起来好了,上线后换个入口又复现。2026 年不少团队已经把死锁日志保留和慢查询采样列进上线检查项,成本不高,但能省下大量回头猜的时间。

几种处理方案的可核对对比

不同方案解决的是不同层面的问题,不能混着上。统一访问顺序适合两个业务入口固定更新同一批行的场景;缩小事务适合事务里混了非数据库操作的场景;补索引适合 where 条件没命中索引、锁范围过大的场景;重试则更多是兜底,不是根因修复。下面按常见交付经验给出可核对对比,具体周期会随代码结构和测试条件变化。

  • 统一访问顺序:改造成本低,常见区间是半天到两天。对固定双入口更新有效,对动态批量更新作用有限。
  • 缩小事务:改造成本中等,常见区间是一两天到一周。需要把远程调用、消息发送移出事务,收益是持锁时间明显缩短。
  • 补索引减少锁范围:成本低到中,经验区间是半天到三天。先核对执行计划和锁等待日志,再决定加联合索引还是调整条件。
  • 加锁超时与幂等重试:改造成本低,常见区间是几小时到一天。适合可重放且幂等的业务,不能替代根因修复。
  • 降低隔离级别:配置调整成本低,但要核对业务是否依赖可重复读语义,影响面可能超出预期。

方案之间不是谁更先进,而是根因是否被覆盖。如果根因是没走索引,只改顺序大概率还会复发;如果根因是入口顺序不一致,只加重试也只是把报错藏起来。可核对的做法是:改完以后在预发环境用相近并发压一轮,观察死锁日志是否还出现同类循环等待。

适用与不适用边界:哪些系统要防,哪些不必上

死锁处理适合高频写入、同一批行被多个入口更新、批量任务与在线请求并发的场景,比如订单状态流转、库存扣减、账户余额变动。若系统读多写少、单条更新且事务极短,死锁概率本身较低,不必为此引入复杂重试框架。边界判断可以看两个数:单位时间同一行更新次数和事务平均持锁时长。

  • 适合:多入口更新同一行、批量更新与在线写入并发、事务内包含多次写操作、存在范围更新。
  • 不必过度设计:几乎只有插入、单条按主键更新、事务内只有一条 SQL、写入并发很低的内部系统。
  • 需要重点核对:可重复读隔离级别下的范围更新、无索引 where 条件、嵌套事务、事务内远程调用。
  • 不适合:把死锁当成单纯代码 bug,不查索引和事务边界就反复改 SQL 顺序或无限加重试。

这里的不适用边界要写清楚:如果业务不允许重复执行,或者重试会带来重复扣款、重复发货,就不能只靠重试兜底,应优先修根因。重试次数和锁等待超时也不是越大越好,设得太长可能把接口响应拖到用户侧超时,常见区间是按业务容忍度和数据库负载调整,而不是照搬某个固定值。

交付现场经验:约束、做法和代价

在 2026 年的一次实际交付里,约束是排期只剩一周、没有条件为每个写接口做锁压测,系统里订单状态和库存扣减两个入口会同时更新同一批行,偶发 Deadlock found。做法是先给高频写路径补执行计划核对和死锁日志保留,把两个入口的更新顺序统一到同一字段顺序,再把事务里的外部调用移到事务外,并给可重放的写操作加幂等重试。结果是死锁报错从每天若干次降到零星,接口超时投诉也少了;代价是低频的批量任务路径仍可能偶发,需要靠监控告警补位,无法做到所有路径都覆盖。经验区间是:高频写路径一到三天能先稳住,完整治理往往要一到两周,取决于入口数量和历史事务写法。

现场常踩的坑还有:只把报错接口加重试,死锁日志没开,下次复发还是查不到;where 条件字段没索引,以为行锁只锁一行,实际范围锁扩大;事务里调第三方接口,第三方超时导致事务持锁时间成倍拉长。合格线可以按四条核对:高频写路径有执行计划核对、事务边界清晰、死锁日志可查、重试有幂等兜底。

  • 反例一:只把报错接口加重试,死锁日志没开,下次复发还是查不到。
  • 反例二:where 条件字段没索引,以为行锁只锁一行,实际范围锁扩大。
  • 反例三:事务里调第三方接口,第三方超时导致事务持锁时间成倍拉长。
  • 合格线:高频写路径有执行计划核对、事务边界清晰、死锁日志可查、重试有幂等兜底。

常见问题

数据库报 Deadlock found,重试几次不报了就没事了吗?

偶发死锁重试可能成功,但重试只是兜底。若不查索引和事务边界,流量一上来还会复发,并可能把问题掩盖成偶发超时。

把两条更新语句换个顺序,为什么有时有效有时没用?

换顺序只对两个事务固定以相反顺序访问同一批行有效。若根因是没走索引或事务过长,换顺序不会改变锁范围。

死锁日志里只有一条 SQL,能定位问题吗?

不够。需要两个事务的持有锁和等待锁信息,再结合执行计划与事务边界,才能还原循环等待路径。

降低隔离级别能减少死锁吗?

从可重复读调到读已提交可减少间隙锁,常见做法能降低部分死锁,但要先确认业务不依赖可重复读语义。

单条按主键更新也会死锁吗?

概率较低,但同一主键被两个长事务交叉更新、或事务里还夹了其他写操作时仍可能发生,不能完全排除。


如果你正在处理偶发死锁,建议先确认死锁日志是否可查,再挑一条高频写路径核对执行计划和事务边界。死锁重试适合可重放且幂等的业务;若业务不允许重复执行,应优先修根因而不是加重试。读多写少、事务极短的内部系统不必为此引入复杂框架,按实际写入并发量判断即可。2026 年可先把高频写路径的证据链补齐,再决定投入多少改造。

有类似的项目需求?
联系我们,获取一对一项目参考方案
获取方案
准备好开始了吗,
那就与我们取得联系吧!
13370032918
了解更多服务,随时联系我们
请填写您的需求
您希望我们为您提供什么服务呢
您的预算
联
系
微信二维码
扫码添加客服微信
专业对接各类技术问题
联系电话
13370032918 (金经理)
电话若占线或未接到、就加下微信
联系邮箱
349077570@qq.com
提交成功
感谢您的信任,我们会尽快与您联系!
为您推荐以下案例