ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ChatGPT、Codex排查实录:没有死锁,为什么MySQL还是报“Lock wait timeout exceeded”?

ChatGPT、Codex排查实录:没有死锁,为什么MySQL还是报“Lock wait timeout exceeded”? 线上订单接口突然开始报错Lock wait timeout exceeded; try restarting transaction第一反应通常是MySQL是不是死锁了于是去找Deadlock日志。结果没有。数据库CPU不高连接数也没打满慢SQL里甚至看不到特别夸张的查询。更奇怪的是同一条SQL单独复制出来执行只要几十毫秒。可一到线上并发场景它就能卡几十秒最后直接超时。这种问题特别容易被误判成SQL性能不好。但真正需要先确认的是这条SQL究竟是“执行得慢”还是“根本没有机会执行”。一、先给核心判断Lock wait timeout不等于Deadlock这两个错误经常被混在一起。Deadlock更像这样事务A拿着资源1等资源2。事务B拿着资源2等资源1。两边形成循环等待。MySQL发现这种状态以后通常会主动选择一个事务回滚。而Lock wait timeout exceeded更常见的情况是一个事务一直持有锁另一个事务只能在后面等。没有形成循环。所以MySQL不会立刻判断成死锁。它就一直等。直到超过锁等待时间才报Lock wait timeout exceeded所以没有Deadlock日志完全不代表没有锁问题。二、一个很典型的现场假设有两个请求。事务AUPDATE orders SET status PAYING WHERE id 10086;这条SQL本身只需要几毫秒。但是事务A执行完以后并没有马上提交。业务代码继续做调用支付接口。写日志。查询其他数据。甚至发起一次远程RPC。整个事务持续了30秒。这30秒里订单10086对应的锁一直没有释放。这时候事务B也执行UPDATE orders SET status CANCELLED WHERE id 10086;事务B不是SQL慢。它是在等事务A释放锁。如果最终等待时间超过MySQL允许的Lock Wait Timeout就会报错。所以你把事务B的SQL复制到数据库里单独执行很可能20ms完成。这并不能证明线上没有问题。因为你已经脱离了当时那个锁竞争现场。三、第一件事不是EXPLAIN而是找“谁在等、谁在挡”很多人看到数据库异常第一个动作就是EXPLAIN这在慢查询里没问题。但遇到Lock Wait Timeout优先级应该换一下。先回答Waiting Transaction是谁Blocking Transaction又是谁在MySQL 8环境里可以结合performance_schema和sys里的锁等待信息去看。你真正想找到的是一条关系Transaction B 正在等待 ↓ Transaction A 持有某个锁 ↓ A已经持续了28秒一旦找到Blocking Transaction问题通常就已经解决了一半。因为报错的是B。真正应该查的却可能是A。四、为什么日志经常把你带到错误方向假设应用日志里报错的是OrderCancelService Lock wait timeout exceeded于是大家开始检查取消订单逻辑。但真正持锁的可能是PaymentCallbackService甚至是另一个后台任务。这也是锁等待问题最麻烦的一点出错位置不一定是根因位置。等待者只是在最后一个倒霉的人。真正的问题通常藏在那个长时间没有提交的Blocking Transaction里。所以看到Lock Wait Timeout以后不要只沿着异常堆栈往上查。还要横向找同一时间还有哪个事务占着目标记录。五、最常见的根因之一事务里塞了外部调用这是我会优先检查的一类代码。例如开启事务 ↓ 更新订单 ↓ 调用第三方支付 ↓ 调用库存服务 ↓ 写数据库 ↓ 提交事务问题在于数据库锁的生命周期被远程调用的响应时间拖长了。第三方接口平时100ms。偶尔变成5秒。甚至超时15秒。那么你的数据库锁也跟着被持有15秒。所以一个特别重要的工程原则是不要让数据库事务无意义地覆盖慢速外部IO。HTTP。RPC。MQ同步确认。文件操作。这些都应该重新审视是不是必须放在事务里面。六、第二个常见原因一个“看起来很快”的事务其实很长有时候SQL都不慢。但是业务事务跨度特别大。比如BEGIN 查询订单 更新订单 查询用户 更新余额 查询优惠券 更新优惠券 写操作日志 COMMIT每条SQL10ms。20ms。30ms。单看都正常。但整个事务可能持续几百毫秒甚至几秒。并发一高同一批热点数据互相竞争等待时间就会快速放大。所以排查时不能只看SQL Duration。还要看Transaction Duration。这两个指标不是一回事。七、索引没用好也可能把锁范围放大假设原本你只想更新一条记录。但WHERE条件没有合适索引。MySQL为了找到目标数据需要扫描更多记录。这不仅意味着查询成本上升。在某些更新场景下还可能让锁竞争范围比你想象得更大。所以遇到锁等待以后依然需要看执行计划。但顺序应该是先找Blocking Transaction再看为什么它会锁住这么久、这么多。而不是一开始就只盯着“有没有走索引”。八、还有一种特别隐蔽程序忘记提交或者回滚比如应用开启事务以后中途出现异常。正常路径有Commit。但异常路径没有正确Rollback。连接又没有马上结束。于是这个事务可能继续挂着。它持有的锁也一直存在。这种现场经常表现为系统刚启动一切正常。运行一段时间以后偶发Lock Wait Timeout。重启应用以后又暂时恢复。如果你发现某个事务长期处于活跃状态却几乎没有继续执行SQL就要特别警惕Open Transaction没有正常结束。九、最快排查顺序我建议直接按这7步走以后再碰到Lock wait timeout exceeded可以直接照这个顺序。第一步记录准确报错时间最好精确到秒。锁等待现场是动态的时间对不上后面很多证据都没法关联。第二步找到Waiting Transaction确认哪条SQL正在等待。它等了多久。第三步找到Blocking Transaction重点看谁持有锁。这个事务已经活了多久。第四步查看Blocking事务执行了什么不要只看当前SQL。要看它整个事务里做了什么。有没有HTTP。RPC。批量处理。复杂业务逻辑。第五步检查索引和锁范围确认是不是因为访问路径不合理让锁范围扩大。第六步回到业务代码看事务边界看Transactional到底包了多大范围。有没有把外部调用也包进去。第七步修复以后重新制造并发场景不能只看单条SQL执行成功。要重新做并发验证。十、为什么直接“调大innodb_lock_wait_timeout”通常不是修复看到超时最简单的想法就是现在等50秒。那改成100秒。200秒。是不是就好了有些场景确实能降低报错数量。但如果根因是长事务。事务里调用RPC。连接未正确提交。热点行竞争。索引不合理。那你只是让后面的事务等得更久。问题没消失。接口反而可能从50秒报错变成100秒以后再报错。对用户体验更差。所以调大Timeout最多应该是容量和业务策略调整的一部分。不能代替根因分析。十一、重试也不能乱加MySQL报try restarting transaction很多人看到以后就直接失败重试3次。但这里有一个前提当前操作必须适合重试。如果是普通查询。幂等更新。重试通常比较容易控制。但如果事务里包含扣款。发消息。调用第三方接口。生成订单。那盲目重试可能产生重复副作用。所以更合理的是先解决长期持锁问题。再对明确可重试的数据库操作增加有限次数。随机退避。幂等保护。而不是把所有Lock Wait Timeout都交给Retry掩盖。十二、一个特别值得检查的地方Transactional是不是包太大了Java项目里经常看到Transactional public void processOrder() { 查询订单 更新状态 调用远程服务 执行业务计算 更新库存 写日志 }从业务逻辑上看很完整。但从数据库角度看这个事务的锁可能从第一次UPDATE开始就一直持有到整个方法返回。如果里面某个RPC突然慢了5秒锁也跟着多持有5秒。所以优化时经常不是把SQL改得更漂亮。而是缩小Transaction Boundary。让真正需要原子性的数据库操作留在事务中。外部调用尽可能不要占着数据库锁等待。十三、热点数据也会把问题放大例如同一个库存记录。同一个活动配置。同一个账户余额。同一个任务状态。大量请求同时更新。即使每个事务只持锁100ms并发达到一定程度以后后面的请求还是会形成长队。这时候需要考虑的不只是数据库参数。还可能包括减少热点更新。拆分热点行。串行化关键路径。使用队列。乐观锁。业务层限流。所以锁等待最终有时候并不是一个“MySQL配置问题”。而是数据访问模型本身的问题。十四、修完以后怎么验证这类问题最忌讳改完代码单测一遍然后说好了。真正应该复现的是原来的并发条件。比如原来20个请求同时更新同一个订单或同一批热点记录。那修完以后继续制造相同并发。然后观察Lock Wait数量。事务持续时间。P95 / P99。Blocking Transaction。接口错误率。如果事务边界缩短以后锁等待明显下降。P99同步下降。也不再出现长时间Blocking事务这才算真正修复。十五、这类数据库排查ChatGPT、Codex怎么用更有效不要只丢一句MySQL报Lock wait timeout怎么解决更好的方式是把Evidence一起给出来报错时间。Waiting SQL。Blocking SQL。事务持续时间。表结构和索引。相关业务代码。Transactional范围。并发场景。这样ChatGPT、Codex才能帮助你判断到底是长事务。锁范围扩大。事务边界过大。还是热点数据竞争。AI最有价值的地方不是替你猜根因。而是把分散的数据库证据和业务代码串成一条完整因果链。十六、Plus和Pro怎么判断如果你只是偶尔让ChatGPT、Codex分析一段锁等待。看一个事务。检查几条SQL。帮你缩小一次Transactional范围。这种集中型任务Plus通常已经够用。如果你的日常已经是持续分析MySQL锁。大量应用日志。长事务。Trace。Java代码。连接池。并发压测结果。并且一次事故需要不断让Codex修改代码、跑测试、重新验证这种长时间、多轮工程排障越来越频繁那Pro会更适合。判断标准不是Lock wait timeout难不难。而是ChatGPT、Codex是不是已经长期参与你的完整数据库排障链路。最后MySQL没有Deadlock为什么还是会报Lock wait timeout exceeded因为没有形成死锁不代表没有事务长期占着锁。真正需要排查的不是“这条报错SQL为什么这么慢”而是它到底在等谁。完整排查链应该是报错时间 → Waiting Transaction → Blocking Transaction → Transaction Duration → SQL与索引 → 事务边界 → 并发验证尤其要警惕事务里有HTTP / RPC。事务长期不提交。异常路径未正确回滚。热点记录高并发更新。这些场景。一旦把“等待者”和“阻塞者”分开看很多看起来玄学的Lock wait timeout exceeded其实都能很快落到一个非常具体的事务上。持续分享 Codex、大模型开发与 AI 编程实战内容也整理了稳定的 Plus/Pro 订阅渠道有需要下方可自取。
返回列表