mysql 5.7+中group by不默认排序,explain显示using filesort主因是order by或隐含排序需求与索引不匹配;建索引需满足最左前缀、等值where、聚合函数限制等条件才能触发loose index scan。

MySQL 5.7 中 GROUP BY 不再默认排序,但旧 SQL 迁移或未适配时仍可能触发 Using filesort 和 Using temporary——这不是 bug,而是语义和索引使用不匹配的信号。
为什么 EXPLAIN 显示 Using filesort?
这是最常被误读的性能线索。MySQL 5.7+ 的 GROUP BY 本身不强制排序,但一旦你同时写了 ORDER BY,或查询中隐含了排序需求(比如分页、前端要求固定顺序),优化器就会尝试走索引排序路径;若索引不匹配,就 fallback 到 Using filesort。
- 常见诱因:SELECT 中混入了非分组字段(如
SELECT name, COUNT(*) FROM t GROUP BY dept_id),且name无索引或不在联合索引覆盖范围内 - 另一个关键点:WHERE 条件用了索引 A,GROUP BY 用了索引 B,MySQL 无法复用——它只能选其一,剩下那个就得靠 filesort 补救
- 注意:即使没写
ORDER BY,某些客户端或 ORM 自动生成的语句会悄悄加上,务必检查实际执行的 SQL
建什么索引才能让 GROUP BY 走 Loose Index Scan?
松散索引扫描(Loose Index Scan)是 5.7 支持的高效分组方式,但它对索引结构极其敏感,不是“有索引就行”。
- 必须是
BTREE索引(HASH不行) -
GROUP BY字段必须严格匹配索引最左前缀,不能跳列、不能换序。例如索引是(status, user_id, create_time),则GROUP BY user_id无效,GROUP BY status, user_id才有效 - 如果带 WHERE,条件列必须是索引的前导列且为等值(如
WHERE status = 'active'),范围查询(>,BETWEEN)会退化为 Tight Index Scan,性能下降明显 - 聚合函数仅限
MIN()/MAX()且作用于紧邻GROUP BY后的同一索引列,否则无法触发 Loose 扫描
ORDER BY NULL 真的还管用吗?
在 MySQL 5.7 中,ORDER BY NULL 依然有效,但它的作用对象变了:它不是禁用“GROUP BY 隐式排序”(因为该行为已移除),而是明确告诉优化器“这个查询结果顺序无关,别费劲排序”,从而避免优化器误判并选择低效的排序索引。
- 适用场景:你只关心聚合结果,不 care 返回顺序,且 EXPLAIN 显示 Extra 里有
Using filesort - 写法必须紧接在
GROUP BY后:GROUP BY category_id ORDER BY NULL,多一个空格或换行不影响,但不能写成ORDER BY NULL, category_id - 副作用:如果后续加了
LIMIT,MySQL 可能无法利用索引做 early-termination,需配合SQL_CALC_FOUND_ROWS或改用子查询控制
什么时候该用 SQL_BIG_RESULT?
当 GROUP BY 结果集很大(比如千万级唯一分组键),而 tmp_table_size 又偏小,MySQL 会在内存临时表撑爆后反复销毁重建,反而比直接走磁盘更慢。这时 SQL_BIG_RESULT 是个务实选择。
- 触发条件:EXPLAIN 中 Extra 出现
Using temporary; Using filesort,且预估分组数远超tmp_table_size / avg_row_length - 写法:
SELECT SQL_BIG_RESULT COUNT(*), dept_id FROM t GROUP BY dept_id——提示必须放在SELECT关键字后、其他字段前 - 风险点:小结果集下反而变慢;若磁盘 I/O 已是瓶颈,加了也白加;需同步调大
sort_buffer_size配合磁盘排序效率
真正卡住性能的往往不是语法本身,而是 WHERE、GROUP BY、ORDER BY 三者索引诉求冲突时,优化器被迫降级到临时表+文件排序的组合。盯紧 EXPLAIN 的 Extra 列,比背口诀管用得多。











