直接回答:死锁是循环等待:事务 A 持锁 1 等锁 2,事务 B 持锁 2 等锁 1。数据库检测到环即杀一个事务(牺牲者)。分析方法三步:拿死锁日志还原“各事务持有什么、在等什么”;找出加锁顺序冲突的代码路径;用统一加锁顺序、缩短事务、降低隔离级别或拆分大事务消除环路。
展开解析:日志解读实战:MySQL 的 SHOW ENGINE INNODB STATUS 的 LATEST DETECTED DEADLOCK——事务段列出持有锁(HOLDS)与等待锁(WAITING FOR),锁类型看记录锁/间隙锁/next-key 锁与索引名(二级索引与主键索引的锁都要看——UPDATE 二级索引列会先锁二级索引再锁主键,不同 WHERE 条件走不同索引是经典死锁源);PG 的 deadlock detected 日志列各进程等待的锁与正在执行的语句。高频死锁模式:顺序交叉——A 先更订单表后更库存,B 反之,解法是全系统统一资源访问顺序(按主键排序批量加锁);间隙锁交叉——RR 隔离下范围查询的间隙锁互相覆盖,解法 RC 隔离或改用唯一索引等值查询;同一行热更新——库存扣减,解法队列化或乐观锁(version 列重试)。工程防御:死锁不可避免时让重试成为标配——死锁是被杀事务的错误码(MySQL 1213、PG 40P01),应用层捕获后整体重试(幂等前提下);监控死锁频率(information_schema.innodb_metrics 或 PG 的 deadlocks 计数),频率上升先于故障报警。认知收束:死锁不是 bug 是并发控制的必然副产品,目标是“低频加可重试”,追求零死锁常付出更大并发度代价。
追问方向:为什么 RR 隔离级别的间隙锁是死锁放大器?乐观锁重试风暴怎么防?
(约 490 字)