Using filesort说明MySQL明确放弃索引有序性,必须额外排序;常见原因包括ORDER BY字段不在索引最左前缀、范围查询截断索引、函数/表达式破坏有序性、ASC/DESC混用(5.7不支持)及非驱动表排序等。
它不是“可能慢”,而是MySQL明确放弃了索引的物理顺序,必须额外启动一套排序流程——不管数据量多小,只要出现,就代表排序逻辑已脱离索引结构。这个过程默认先用sort_buffer_size内存排序,超限才落盘生成MY****临时文件,但IO和CPU开销已随行数非线性飙升。
索引存在 ≠ 排序可用。常见真实原因包括:
WHERE用了范围条件(如created_at > '2023-01-01'),导致索引只能用到前缀,后续ORDER BY字段无法复用有序性(status, created_at),但查询写WHERE created_at > ... ORDER BY id——created_at不是最左字段,索引根本无法定位起始位置ORDER BY UPPER(name)或ORDER BY a + b:索引存的是原始值,计算后无法对齐INDEX (a ASC, b DESC),优化器也视而不见SELECT *强制回表,即使ORDER BY id走主键,物理行顺序也被打乱,内存里还得重排核心是让「过滤字段 + 排序字段」在索引中连续、顺序一致、无跳过、无函数干扰:
WHERE status = 1 ORDER BY created_at DESC,就建INDEX (status, created_at DESC)——注意DESC在MySQL 8.0+才生效,5.7必须删掉name和email,且不想回表,扩展为INDEX (status, created_at DESC, name, email),但别把id单独加在前面,主键已隐含在二级索引末尾SELECT *搭配ORDER BY,尤其含TEXT/BLOB字段时,max_length_for_sort_data阈值容易被突破,迫使优化器退到two-pass模式,必然触发Using filesortANALYZE TABLE t更新统计信息——优化器可能因行数预估偏差,直接跳过你刚建的索引这通常意味着MySQL先构建了临时表(比如多表JOIN后无法流式输出),再对这张临时表排序。此时调大sort_buffer_size基本无效——排序对象是临时表,而临时表本身已因设计问题被迫生成。
优先级很明确:先消灭Using temporary,比如加覆盖索引确保所有JOIN条件 + 排序字段 + 查询字段都在一个索引里,或重构JOIN顺序让驱动表能主导排序;等Using temporary消失后,Using filesort往往也随之消失。
最容易被忽略的复杂点是:索引字段顺序必须严格匹配查询执行路径——先WHERE过滤,再ORDER BY排序,最后SELECT返回。中间任何一环错位,都会让整个索引对排序失效。