order by 本身不直接导致长锁,但索引设计不当会引发 filesort、全表扫描和大量随机 i/o,延长锁持有时间,加剧高并发下的锁等待。

Order By 本身不会导致长锁定,但不当索引设计会让它触发 filesort + 全表扫描 + 大量随机 I/O,间接拖慢事务、加剧锁等待 —— 尤其在高并发 UPDATE/DELETE 场景下。
为什么 ORDER BY 会拖慢锁表现
MySQL 的行锁(如 InnoDB 的 record lock)只在实际访问的索引记录上加锁。但当 ORDER BY 无法走索引时:
- 优化器可能选择全表扫描(或大范围索引扫描),导致大量无关记录被临时访问、甚至加间隙锁(gap lock)
- filesort 阶段若超出
sort_buffer_size,会写磁盘临时文件,延长语句执行时间 → 锁持有时间自然拉长 - 如果语句带
FOR UPDATE或嵌套在事务中,锁会持续到事务结束,而慢排序让这个窗口变宽
哪些 ORDER BY 实际无法用索引排序
即使建了索引,以下情况仍会触发 Using filesort,且容易引发长锁:
-
ORDER BY ABS(created_at):函数包裹字段,索引失效 -
WHERE status IN ('paid', 'shipped') ORDER BY updated_at:IN是范围条件,之后的排序列无法复用索引顺序(除非是覆盖索引且updated_at是索引第二列) -
ORDER BY a ASC, b DESC:MySQL 5.7 不支持混合方向;8.0+ 虽支持,但索引必须显式声明DESC,否则无效 -
SELECT * FROM t WHERE shop_id = 1001 ORDER BY created_at DESC:索引为(shop_id, created_at)时,5.7 下DESC不生效;8.0+ 必须建为(shop_id, created_at DESC) -
JOIN ... ORDER BY non_driving_table.col:排序字段来自非驱动表,且该表没建对应索引 → 只能 filesort 后再归并
怎么建索引才能让 ORDER BY 真正“免排序”
核心是让索引结构匹配查询的过滤 + 排序逻辑,且避免回表放大 I/O:
- 等值条件字段(
=、IN单值)放最左;多个等值条件顺序可调,但需和WHERE中实际使用顺序尽量一致 - 排序字段紧接其后,方向必须与
ORDER BY严格一致(8.0+ 才认DESC声明) - 如果想避免回表,把
SELECT中所有非主键字段追加到索引末尾(覆盖索引);但注意索引宽度,别过度膨胀 - 反例:
INDEX (created_at, shop_id)对WHERE shop_id = ? ORDER BY created_at完全无效 —— 排序字段不在索引后缀位置
例如这张订单表:orders(shop_id, status, created_at, order_no, amount),常用查询是:
SELECT order_no, amount FROM orders WHERE shop_id = 1001 AND status = 'paid' ORDER BY created_at DESC LIMIT 10;
最优索引是:INDEX (shop_id, status, created_at DESC, order_no, amount)。
检查和验证是否真走了索引排序
别只看有没有索引,要确认执行计划里是否真的跳过了排序阶段:
- 运行
EXPLAIN FORMAT=TRADITIONAL your_query - 重点看
Extra字段:Using index表示覆盖索引 + 有序返回;Using filesort就是失败 - 如果
type是index或range,且key显示用了你的复合索引,再结合Extra判断才可靠 - 注意:即使
Extra没写Using filesort,若rows值极大,说明索引范围扫描太宽,仍可能因 I/O 拖慢锁表现
复杂点在于:同一个索引,在不同 WHERE 条件组合下,优化器可能弃用它去选全表扫描 —— 所以必须针对具体查询验证,不能靠“有索引就安心”。











