order by本身不加锁,但索引设计不当会触发filesort和全表扫描,导致扫描行数激增、锁持有时间延长及间隙锁范围扩大,尤其在高并发或长事务中加剧锁等待。

ORDER BY 本身不加锁,但当它触发 filesort 或无法利用索引排序时,会显著拉长事务持有锁的时间——这不是语法问题,而是执行路径导致的连锁反应。
为什么 ORDER BY 会让锁“卡住”更久
InnoDB 的行锁只加在实际访问的记录上,但 ORDER BY 若走不了索引,优化器就可能选择全表扫描或大范围索引扫描,结果是:本不需要的记录也被读取、甚至被加间隙锁(gap lock);更关键的是,filesort 阶段若超出 sort_buffer_size,就会写磁盘临时文件,整个语句执行时间被拖长,而锁会一直持有着,直到语句结束(或事务提交)。
哪些 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+ 必须显式建为(a ASC, b DESC),否则仍filesort -
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非驱动表字段,且该表没对应索引 → 只能先filesort再归并
怎么建索引才能让 ORDER BY 真正“免排序”
核心是让索引结构严格匹配查询的 WHERE + ORDER BY 逻辑,同时控制回表开销:
- 等值条件字段(
=、单值IN)放最左;多个等值条件顺序尽量和WHERE中一致 - 排序字段紧接其后,方向必须与
ORDER BY完全一致(ASC/DESC都要显式声明) - 想避免回表?把
SELECT中所有非主键字段追加到索引末尾(覆盖索引),但注意索引宽度别超过 3000 字节 - 反例:
INDEX (created_at, shop_id)对WHERE shop_id = ? ORDER BY created_at完全无效 —— 排序字段不在索引后缀位置
ORDER BY 慢 + 锁长,背后真正危险的是事务边界
最常被忽略的一点:即使 ORDER BY 本身只耗几百毫秒,如果它嵌套在未提交的事务里(比如 BEGIN 后执行完查询但没 COMMIT),那锁会一直挂着。查 information_schema.INNODB_TRX 时看到 trx_query 为空、duration_sec > 60,基本就是应用层忘了提交。这种“空闲事务”比慢查询更致命——它不干活,但死死攥着锁。











