group by 必须用复合索引而非单列索引,因mysql和postgresql仅支持最左前缀匹配,要求索引列顺序与group by字段顺序完全一致;单列索引无法满足多字段分组的有序归并需求,必然触发using temporary和filesort。

GROUP BY 为什么必须用复合索引,而不是单列索引
单列索引对多字段分组基本无效。MySQL 和 PostgreSQL 都只支持最左前缀匹配,且要求索引列顺序与 GROUP BY 子句中字段顺序完全一致。比如 GROUP BY region, city, category,只有 (region, city, category) 或 (region, city, category, amount) 这类索引能直接跳过排序和临时表;而 (city, region)、(region, category) 甚至三个单列索引,都会触发 Using temporary。
常见错误是给每个分组字段单独建索引——这不仅不加速分组,还增加写入延迟和磁盘占用。
- 索引列顺序必须和
GROUP BY字段出现顺序严格一致 - WHERE 条件字段要前置:如
WHERE status = 'active' GROUP BY user_id,索引应为(status, user_id) - 高选择性字段(如
status)放左边,低选择性字段(如gender)放右边,避免因前缀区分度低导致索引失效
覆盖索引怎么建才真正起作用
覆盖索引不是“把 SELECT 所有字段都塞进索引”,而是让数据库不用回表就能拿到全部所需数据。关键看 EXPLAIN 输出:key 显示用了哪个索引,且 Extra 里不能有 Using temporary 或 Using filesort,同时 SELECT 和 GROUP BY 字段全在索引定义中。
例如:SELECT dept_id, COUNT(*), AVG(salary) FROM emp WHERE active = 1 GROUP BY dept_id,理想索引是 (active, dept_id, salary)——active 过滤,dept_id 分组,salary 支持 AVG() 聚合,三者都在索引里,无需访问主表行。
- 别在索引末尾硬加无关字段,比如只查
dept_id, COUNT(*)却建(active, dept_id, name, email),徒增索引体积 - 隐式类型转换会让覆盖失效:比如
user_id BIGINT字段,但查询里写WHERE user_id = '123',索引就走不了 - MySQL 8.0+ 支持函数索引,可建
INDEX idx_date ON orders((DATE(create_time))),老版本只能靠生成计算列 + 索引
EXPLAIN 里出现 Using temporary 就得立刻处理
Using temporary 不是警告,是已经失败的信号:MySQL 正在用磁盘临时表做分组,I/O 直接拖垮性能。触发原因通常是分组字段无索引、索引未覆盖、或数据量超出 tmp_table_size 内存限制。
检查方式很简单:EXPLAIN FORMAT=TRADITIONAL SELECT ... GROUP BY ...,重点盯 Extra 列。一旦看到 Using temporary,基本可以确定当前索引没生效,或者查询逻辑本身在强迫数据库走低效路径。
-
type是ALL或index?说明没走有效索引,得重建复合索引 -
key_len是否合理?比如字段是VARCHAR(255),但只用了前 10 个字节,key_len却显示 765,说明索引定义和实际查询不匹配 -
Extra里有没有Using where?如果没有,WHERE条件很可能没进索引,过滤动作被挪到了分组后
ORDER BY 和 GROUP BY 混用时索引容易踩坑
当语句带 ORDER BY 且和 GROUP BY 字段不一致时,比如 SELECT user_id, COUNT(*) FROM orders WHERE status ='paid' GROUP BY user_id ORDER BY COUNT(*) DESC,ORDER BY COUNT(*) 无法走索引,MySQL 仍会生成临时表再排序。
一个索引无法同时优化 GROUP BY 和非前缀的 ORDER BY。MySQL 不支持这种混合优化。
- 如果
ORDER BY字段和GROUP BY完全一致(如都按user_id),加ORDER BY NULL反而能省掉默认隐式排序开销 - 如果
ORDER BY是聚合结果(如COUNT(*)、AVG(salary)),基本没法走索引,只能靠子查询提前聚合,或应用层排序 - 混合方向排序(如
ORDER BY created_at ASC, department DESC)需 MySQL 8.0+ 降序索引支持,否则仍会触发Using filesort
GROUP BY email),这时候索引作用有限,得考虑降维(提取域名)、换引擎(ClickHouse/StarRocks)或预聚合。











