GROUP BY性能差主因是MySQL被迫使用临时表和文件排序,判断依据为EXPLAIN中type非ALL/index且Extra无Using temporary/Using filesort。

GROUP BY没走索引?先看EXPLAIN里的type和Extra
执行慢不是因为GROUP BY语法本身,而是MySQL被迫用临时表+文件排序。关键判断依据只有两个:type不能是ALL或index,Extra里不能出现Using temporary或Using filesort。一旦出现,说明索引没对上,或者查询结构破坏了索引可用性。
常见误判:给user_id建了单列索引,但查询带WHERE status = 'paid',结果还是全表扫描——因为过滤和分组没共用一个索引。
- 必须用
EXPLAIN FORMAT=TREE(MySQL 8.0+)或EXPLAIN ANALYZE(PostgreSQL)验证,不能凭感觉 - 如果
rows显示扫描行数远大于实际分组数(比如扫500万行只出200个分组),说明WHERE没有效过滤 -
NULL值会影响索引选择,加WHERE col IS NOT NULL有时能激活索引
索引字段顺序错了,再宽的索引也没用
联合索引不是“包含所有字段”就行,顺序决定它能不能被GROUP BY复用。核心规则就一条:WHERE条件字段最左,GROUP BY字段紧随其后,SELECT中非聚合字段放末尾。
例如查询:SELECT user_id, MAX(created_at), SUM(amount) FROM orders WHERE status = 'paid' GROUP BY user_id,理想索引是(status, user_id, created_at, amount)。
- 错序示例:
(user_id, status)对这个查询基本无效——MySQL无法先按user_id排序再去过滤status - 如果还带
ORDER BY COUNT(*) DESC LIMIT 10,索引再好也救不了;此时Using filesort是必然的,得换预聚合方案 - 别把
remark、content这类大字段塞进索引,写入开销和缓存压力会陡增
GROUP BY字段上用了函数?索引直接失效
GROUP BY DATE(create_time)、GROUP BY UPPER(name)、WHERE id = '123'(id是INT)——这些写法会让索引彻底作废,哪怕字段本身有索引。
替代方案不是“换个函数”,而是前置计算或结构改造:
- 用生成列:
ALTER TABLE logs ADD COLUMN create_date DATE AS (DATE(create_time)) STORED,再对create_date建索引 - 时间范围查询优先用区间:
WHERE create_time >= '2024-01-01' AND create_time ,再<code>GROUP BY create_date - 类型不一致时显式转换:
WHERE id = CAST('123' AS UNSIGNED),比隐式转换更可控
为什么加了ORDER BY NULL还是慢?可能漏了聚合字段覆盖
ORDER BY NULL只能禁用结果集排序,但解决不了回表和临时表问题。真正卡住的,往往是SELECT里那些非分组字段没被索引覆盖。
比如SELECT dept_id, AVG(salary), MAX(hire_date) FROM emp GROUP BY dept_id,只建(dept_id)索引没用——AVG()和MAX()需要读取每组所有salary和hire_date值,必须让它们也进索引。
- 索引应为
(dept_id, salary, hire_date),且salary和hire_date在B+树里天然有序,MAX/MIN可快速定位 -
COUNT(*)和COUNT(非空列)在覆盖索引下性能几乎一样;但COUNT(col)(col允许NULL)仍需逐行判空,无法完全跳过数据页 - 如果业务只要Top 10分组,别硬扛全量分组,改用窗口函数或物化汇总表


















