MySQL死锁主因是事务以不同顺序锁定多行,而非并发高或同改一行;排序更新(ORDER BY)可统一加锁顺序,但需索引支持且执行计划须验证。

死锁不是并发高才发生,而是更新顺序不一致直接触发
MySQL 的 UPDATE 死锁,根本原因不是“同时改同一行”,而是多个事务以不同顺序锁定多行。比如事务 A 先锁 id=5 再锁 id=10,事务 B 反过来先锁 id=10 再锁 id=5 —— 这时 InnoDB 就会立刻选一个事务回滚,报错 Deadlock found when trying to get lock。
排序更新(ORDER BY)是强制统一加锁顺序的最简单手段,但必须满足两个前提:更新条件能走索引、排序字段本身在索引覆盖范围内。
- 没索引的
ORDER BY会触发 filesort,不仅慢,还可能让优化器放弃使用索引扫描,导致全表加锁或锁范围扩大 - 如果
WHERE条件用status = 'pending',但没给status建索引,ORDER BY id也救不了你——InnoDB 可能先扫全表再排序,锁住所有匹配行 - 复合索引要注意最左前缀:想按
created_at排序更新,但只对(user_id, created_at)建了索引,而WHERE只用了user_id,那ORDER BY created_at才有效;如果WHERE是status,这个索引就基本没用
UPDATE ... ORDER BY 不是万能的,得看执行计划是否真排序
很多人写了 UPDATE ... WHERE status = 'pending' ORDER BY id LIMIT 100 就以为安全了,但 EXPLAIN 一看发现 type 是 ALL 或 index,Extra 里写着 Using filesort —— 这说明 MySQL 没法用索引完成排序,实际加锁顺序仍是不可控的。
验证方式很简单:
- 对目标语句跑
EXPLAIN FORMAT=TRADITIONAL,重点看key是否命中预期索引、Extra是否含Using filesort - 开启
innodb_print_all_deadlocks = ON,把死锁日志打到 error log,观察死锁时涉及的 SQL 和锁住的记录 ID - 用
SELECT * FROM information_schema.INNODB_TRX查正在运行的事务,结合INNODB_LOCK_WAITS看谁在等谁
批量更新时 LIMIT + ORDER BY 的边界陷阱
LIMIT 在 UPDATE 中不保证“每次取相同批次”,尤其当其他事务正在插入/删除数据时。比如你执行 UPDATE t SET status='processing' WHERE status='pending' ORDER BY id LIMIT 100,第一次拿到 id 1–100,但第二次执行时,如果 id=50 的记录已被另一个事务更新过,它就不再满足 WHERE 条件,结果这次可能拿到 id 101–200 —— 表面看没问题,但若业务依赖“严格分批处理”,就会漏或重。
- 更稳的做法是用游标式更新:记录上一批最大
id,下一批从WHERE id > ? AND status = 'pending' ORDER BY id LIMIT 100 -
LIMIT在 UPDATE 中不支持偏移量(LIMIT 100,100语法错误),所以不能靠 offset 分页 - 如果表有自增主键且写入基本有序,游标法比纯
ORDER BY + LIMIT更可控;但如果主键是 UUID 或写入乱序,就得依赖时间戳字段(且该字段要有索引)
唯一索引冲突也会伪装成死锁
有时候看到 Deadlock found,但实际是两个事务尝试插入/更新同一 UNIQUE KEY(比如重复下单),其中一个被锁等待,另一个先提交,等待者醒来发现约束冲突,报的是 Deadlock 而不是 Duplicate entry —— 这是 InnoDB 的行为特性,不是真死锁。
区分方法看死锁日志里的 TRANSACTION 部分:
- 真死锁:日志中明确列出两个事务各自持有的锁、等待的锁,且锁对象是不同记录
- 唯一键冲突伪装:日志里只有一个事务显示持有
X锁,另一个只显示在等待某个supremum pseudo-record,且错误现场常伴随INSERT ... ON DUPLICATE KEY UPDATE或REPLACE INTO - 这类情况加
ORDER BY没用,得靠应用层幂等控制或提前SELECT ... FOR UPDATE检查
真正难防的是多字段联合更新时的隐式锁顺序,比如同时更新 stock 和 version 字段,又没统一好 WHERE 条件和索引设计——这时候光靠 ORDER BY 很容易漏掉索引间隙锁的干扰。


















