GROUP BY或ORDER BY未走索引会强制触发Using temporary和Using filesort,根本原因是索引未覆盖WHERE、分组/排序字段及SELECT非聚合列,导致MySQL必须建临时表并额外排序;典型表现是EXPLAIN中同时出现这两项,且tmp_table_size与max_heap_table_size不一致会加剧磁盘IO。

GROUP BY 或 ORDER BY 没走索引,强制触发 Using filesort + Using temporary
这是最常见也最容易被忽略的根源。MySQL 在执行 GROUP BY 或 ORDER BY 时,若无法利用索引完成排序或分组,就会在内存中建临时表;一旦结果集超过 tmp_table_size 或 max_heap_table_size(取较小值),就立刻落盘为 MyISAM 临时表,产生大量磁盘 IO。
典型现象:EXPLAIN 输出中同时出现 Using temporary 和 Using filesort;SHOW STATUS LIKE 'Created_tmp_disk_tables'; 值持续上升。
- 字段未建索引,或索引顺序与
GROUP BY列不一致(如索引是(a,b),但写的是GROUP BY b,a) - 对字段用了函数或表达式(如
GROUP BY YEAR(create_time)、ORDER BY UPPER(name)),导致索引失效 - 字符集/排序规则不一致(比如关联字段一个是
utf8mb4_0900_as_cs,另一个是utf8mb4_general_ci),使索引无法用于排序 - 查询中混用
SELECT *和GROUP BY,触发 MySQL 5.7+ 的严格模式报错或隐式排序开销
聚合字段未被索引覆盖,回表放大中间结果集
当 GROUP BY 字段有索引,但 SELECT 中包含非索引列(比如 SELECT name, COUNT(*) FROM user GROUP BY dept_id),MySQL 就得先按 dept_id 分组统计,再回主键查每条记录的 name —— 这会导致中间结果膨胀,临时表体积远超预期。
尤其在 COUNT(DISTINCT ...) 或多字段 GROUP BY 场景下,内存不够时会直接写磁盘,IO 峰值飙升。
- 优先改写为覆盖索引查询:确保
GROUP BY列 + 所有SELECT非聚合列都在同一个联合索引中,例如INDEX(dept_id, name) - 避免在聚合查询中
SELECT *,只取真正需要的字段 - 若必须取大字段(如
TEXT),考虑先GROUP BY得到 ID 列表,再用JOIN或子查询补全详情
聚合函数本身不可优化,且数据分布极不均匀
MIN()/MAX() 在有索引时能快速定位首尾节点,但 COUNT(*)、SUM()、AVG() 等必须遍历所有匹配行。如果 WHERE 条件过滤率极低(比如 WHERE status != 0 匹配了 95% 的行),即使走了索引,扫描行数依然巨大。
更麻烦的是,InnoDB 的 COUNT(*) 不像 MyISAM 那样缓存总行数,每次都要实际遍历——尤其在 MVCC 下还要判断可见性,CPU 和 IO 双重压力。
- 确认
WHERE条件是否真的能高效过滤:用EXPLAIN查看rows和filtered值,filtered接近 100 表示几乎没过滤 - 对高频聚合场景,可建汇总表(如按天预计算用户数),用定时任务维护,避免实时扫大表
- 不要依赖
SELECT COUNT(*) FROM t统计全表——除非真需要精确值;否则用估算值(如SHOW TABLE STATUS中的Rows)更快
临时表引擎配置不合理,小内存逼出大磁盘IO
MySQL 默认用 MEMORY 引擎建内部临时表,但该引擎不支持 TEXT/BLOB 类型,也不支持变长字符串过长(超过 max_heap_table_size 单行限制),一触即溃转磁盘。
很多人只调大 tmp_table_size,却忘了 max_heap_table_size 必须同步调高,否则以二者最小值为准,等于白调。
- 检查当前设置:
SELECT @@tmp_table_size, @@max_heap_table_size; - 生产环境建议设为相同值,例如
256M(需根据可用内存评估) - 若聚合结果含大字段或长字符串,干脆跳过内存阶段,直接强制用
InnoDB临时表(MySQL 8.0+ 支持internal_tmp_mem_storage_engine=INNODB),避免频繁落盘抖动
真正卡住聚合查询的,往往不是“有没有索引”,而是“索引能不能支撑整个聚合流水线”。从 WHERE 过滤 → GROUP BY 分组 → SELECT 取值 → 临时表落盘,每个环节都可能悄悄放大 IO。盯紧 EXPLAIN 里的 type、key、Extra,比盲目调参管用得多。










