ORDER BY 没走索引?先看执行计划里的 Using filesort
只要
EXPLAIN结果中出现
Using filesort,就说明 MySQL 没法用索引直接完成排序,得把数据捞出来再内存或磁盘排序——这是性能瓶颈的明确信号。
常见诱因包括:
ORDER BY字段没建索引,或索引不匹配(比如复合索引
(a, b),却只按
b排序) 查询里混用了
SELECT *和
ORDER BY,导致覆盖索引失效
WHERE条件用了范围查询(如
a > 100),之后的索引字段无法用于排序 排序字段类型与索引字段类型不一致(比如索引是
VARCHAR(50),
ORDER BY却隐式转成
VARCHAR(10))
复合索引怎么建才让 ORDER BY 走索引?
核心原则:索引顺序 =
WHERE等值条件字段 +
ORDER BY字段(且不能有范围条件隔开)。
例如查询:
SELECT id, name FROM users WHERE status = 1 ORDER BY created_at DESC
应建索引:
INDEX (status, created_at)—— 这样能同时满足过滤和排序。
注意这些细节:
如果ORDER BY是
created_at ASC, id DESC,索引也得严格对应方向(MySQL 8.0+ 支持混合方向,但 5.7 只支持全 ASC 或全 DESC)
WHERE中有多个等值条件(
status = 1 AND type = 'vip'),索引字段顺序不影响过滤效率,但建议把高频过滤字段放前 避免在索引里包含太多字段,尤其是大字段(如
TEXT),会拖慢写入和索引体积
分页深翻(OFFSET 大)为什么慢?用游标替代
LIMIT 10000, 20慢,不是因为取 20 行,而是 MySQL 必须先扫描前 10000 行并丢弃——即使有索引,也是“跳着扫”。
更优解是游标分页(cursor-based pagination):
首次查:SELECT id, name, created_at FROM posts ORDER BY created_at DESC LIMIT 20下一页传上一页最后一条的
created_at和
id:
SELECT id, name, created_at FROM posts WHERE created_at关键点:WHERE 条件必须能利用索引,ORDER BY 字段必须是索引前缀
临时表排序撑爆内存?调小 sort_buffer_size
反而更快
很多人以为把
sort_buffer_size调大就能加速排序,实际容易适得其反:单个连接独占该内存,设太大反而导致并发时内存争抢、触发 swap。
合理做法:
线上环境一般设为2M~4M(默认通常是 256K,太小会导致频繁磁盘排序) 确认是否真需要全局调大:先用
SHOW PROFILE FOR QUERY N看
Sorting result阶段耗时占比 更治本的是减少参与排序的数据量——加更精准的
WHERE条件,或改用覆盖索引避免回表
真正卡住的往往不是排序本身,而是排序前没过滤干净的数据流。索引设计和查询写法,比调参重要得多。
