出现Using temporary和Using filesort的根本原因是内存不足、索引未覆盖或字段顺序不匹配,导致MySQL被迫使用磁盘临时表分组和额外文件排序;需创建满足WHERE前置、GROUP BY连续、无函数包裹的复合索引,并配合ORDER BY NULL禁用隐式排序。

为什么会出现 Using temporary;Using filesort?
这是 MySQL 执行计划里最典型的两个性能信号,说明它被迫用磁盘临时表做分组(GROUP BY)和排序(ORDER BY)。根本原因是:内存不够、索引没覆盖、或字段顺序不匹配。MySQL 的 tmp_table_size 和 max_heap_table_size 默认值通常只有 16MB,一旦分组后中间结果超过这个阈值,就会落盘——而磁盘 I/O 比内存慢 100 倍以上。
怎么建索引才能让 GROUP BY 走内存分组?
关键不是“有没有索引”,而是“索引字段顺序是否贴合查询逻辑”。必须满足三个条件:
-
WHERE条件字段前置(比如status = 'active'),再跟GROUP BY字段(比如dept_id),最后是聚合需要的字段(比如salary) - 所有
GROUP BY字段必须连续出现在索引开头,不能跳过((a, c)无法加速GROUP BY b, c) - 避免在分组字段上套函数,比如
GROUP BY DATE(create_time)会直接让索引失效;改用范围查询 + 预计算列
示例:查询「2024 年活跃部门人数」,应建 INDEX idx_status_dept (status, dept_id),而不是单独的 dept_id 索引。
ORDER BY NULL 真的有用吗?
有用,而且立竿见影。MySQL 默认会对 GROUP BY 结果按分组字段自动排序,即使你没写 ORDER BY。如果业务不需要这个排序(比如只是取统计数发给前端渲染),加 ORDER BY NULL 就能跳过这一步,避免触发 Using filesort。
对比:
SELECT dept_id, COUNT(*) FROM emp WHERE status = 'active' GROUP BY dept_id; -- 可能触发 filesort
SELECT dept_id, COUNT(*) FROM emp WHERE status = 'active' GROUP BY dept_id ORDER BY NULL; -- 明确禁用排序
高基数字段分组时最容易踩什么坑?
对 user_id、order_no、ip 这类唯一值极多的字段直接 GROUP BY,哪怕只查 10 万行,也可能生成 10 万个分组桶,内存撑不住,必然落盘。这不是调大 tmp_table_size 能解决的——那是治标。
真正该做的:
- 先降维:把
ip转成city或region再分组 - 用近似算法替代精确聚合:比如
APPROX_COUNT_DISTINCT(user_id)(MySQL 8.0+)或 HyperLogLog(需扩展) - 预聚合:按小时/天建汇总表,把实时
GROUP BY变成简单SELECT
临时表大小调到 64MB 顶多缓解小规模问题;数据模型和查询粒度不合理,才是磁盘排序反复出现的根源。

















