先把当时的情况摆出来。库存服务在 MySQL 8.0 上报 Deadlock found when trying to get lock,两个事务更新的主键完全不同,隔离级别是 RR,表上除主键还有普通索引。
这类报错背后常见的原因有几处,逐个排除就行。范围条件在 RR 下加了间隙锁,两个事务锁的区间重叠。另一种情况是update 走了非唯一索引,锁范围扩大到相邻记录。
另外,事务里先 select 再 update,两边加锁顺序相反。少数情况下是同一个业务存在两条不带排序的批量更新路径。
拉最近一次死锁的完整现场
mysql -e "show engine innodb status\G" | sed -n '/LATEST DETECTED DEADLOCK/,/TRANSACTIONS/p'
然后确认语句走的索引和锁范围
mysql -e "explain update stock set qty=qty-1 where sku='A1001' and warehouse=3\G"
再往下改成按主键排序的批量提交
mysql -e "update stock set qty=qty-1 where id in (1,2,3) order by id;"
评估能否降到 RC 隔离级别
mysql -e "select @@transaction_isolation;"
别只看报错消失,验证要多做一步。同样并发压测一小时不再出现死锁,InnoDB 状态里的锁等待次数明显下降,扣减结果与库存流水对得上。
死锁是并发控制的正常产物,关键是让加锁顺序一致、事务尽量短;降隔离级别能减少间隙锁,但要先确认业务不依赖可重复读。改动前把原配置备份一份,出问题能马上回滚,这比任何技巧都实用。
