直接回答:死锁的经典成因是加锁顺序不一致:事务 A 锁了行 1 想要行 2,事务 B 锁了行 2 想要行 1,互相等待成环,数据库检测到环后选一个牺牲者回滚。变体包括:索引间隙锁在范围更新下的交错、先读再加锁(UPDATE 匹配到不同扫描路径)、以及批量操作按输入顺序加锁而输入乱序。系统性减少死锁的核心是让并发事务按相同顺序接触相同资源。
展开解析:工程清单:固定加锁顺序——批量更新前按主键排序,涉及多表的代码路径统一表的访问顺序;缩短事务——先算好业务逻辑再开事务,网络调用绝不在事务内,大事务拆小;降低锁粒度与强度——能读提交别用可重复读的间隙锁(MySQL RR 转 RC 是常见降死锁手段,代价是接受 RC 语义),能唯一索引等值查就别范围扫;重试机制兜底——死锁错误(MySQL 1213)是预期内的正常事件,应用层捕获后有限次重试即可,别让死锁冒泡成 500。排查工具:MySQL 的 SHOW ENGINE INNODB STATUS 或 performance_schema 的死锁日志,PG 的 deadlock_timeout 日志,看最近死锁涉及的 SQL 与锁等待图,对症下药。
追问方向:间隙锁在什么隔离级别存在?为什么唯一索引等值更新很少死锁?乐观锁如何避免死锁?
(约 450 字)