LIMIT会触发额外行锁,因为其执行在WHERE匹配、排序和加锁之后,InnoDB需逐行扫描并加锁直至满足数量,即使只取1行也可能锁住数百行;ORDER BY无索引支撑时更会扩大锁范围。

为什么 LIMIT 会触发额外行锁?
因为 LIMIT 是在 WHERE 条件匹配、排序、加锁之后才执行的截断操作,不是“先限再查”。MySQL 的加锁行为发生在执行计划阶段,它必须确保结果集的确定性——尤其是配合 ORDER BY 时,InnoDB 会按排序逻辑逐行扫描并加锁,直到凑够 LIMIT 数量为止。这意味着:哪怕你只想要 1 行,也可能锁住几十甚至几百行。
ORDER BY + LIMIT 组合最容易引发过度加锁
当查询含 ORDER BY 且没有覆盖索引支撑时,MySQL 可能被迫走全表扫描或索引回表,在这个过程中每扫描一行就尝试加锁(尤其是用了 SELECT ... FOR UPDATE 或 UPDATE ... WHERE ... ORDER BY ... LIMIT 1 这类语句)。常见错误现象包括:
- 明明只更新 1 条记录,却阻塞了其他事务对无关行的修改
-
SHOW ENGINE INNODB STATUS显示持有大量record lock或gap lock - 死锁日志里出现多个事务在相同索引范围上争抢
根本原因在于:InnoDB 不知道哪一行是最终要返回的第 1 行,只能边扫边锁,直到满足 LIMIT 条件为止。
哪些参数或写法会让锁范围雪上加霜?
以下情况会显著扩大实际锁定的行数:
- 没给
ORDER BY字段建索引,导致无法利用索引有序性,必须排序后取前 N —— 此时可能锁全表匹配行 - 用非唯一字段排序(如
status),相同值之间无确定顺序,InnoDB 可能对整个等值区间加next-key lock - 在可重复读(RR)隔离级别下,
ORDER BY x LIMIT n可能触发间隙锁,锁定排序路径上的空隙 - 写成
UPDATE ... WHERE condition ORDER BY pk LIMIT 1,但condition匹配太多行,即使最后只改 1 行,前面所有候选行都已加锁
怎么验证当前查询到底锁了哪些行?
不能只看 SQL 表面,得看执行时的真实加锁行为:
- 执行
SELECT * FROM information_schema.innodb_trx查正在运行的事务 - 用
SELECT * FROM information_schema.innodb_lock_waits找出谁在等谁 - 关键命令:
SHOW ENGINE INNODB STATUS\G,重点关注 “LATEST DETECTED DEADLOCK” 和 “TRANSACTIONS” 部分里的 lock struct 列表 - 如果启用了
innodb_status_output_locks=ON(MySQL 5.6.16+),状态输出会包含更细粒度的锁信息
真正麻烦的不是 LIMIT 本身,而是它和排序、索引缺失、隔离级别叠加后形成的隐式锁扩散——你看到的是一行更新,背后可能是上百行被锁住,而这点在开发阶段极难察觉。


















