一眼看出GROUP BY是否拖慢查询,直接执行EXPLAIN,重点看Extra列:出现Using temporary或Using filesort即明确信号,说明MySQL未走索引分组,正硬建临时表并排序;再看type为ALL或index、rows接近总行数,表明全表扫描且过滤失效。

怎么一眼看出GROUP BY是不是在拖慢查询
直接跑 EXPLAIN,重点盯 Extra 列:出现 Using temporary 或 Using filesort 就是明确信号——MySQL 没走索引分组,正在内存或磁盘上硬排序+建临时表。这时候哪怕只有 50 万行,查询也可能从 200ms 跳到 8s 以上。
再看 type:要是 ALL 或 index(不是 ref/range),基本等于全表扫描;rows 值如果接近表总行数,说明过滤没生效。
- 别只看 GROUP BY 字段有没有单列索引——
GROUP BY a, b时,INDEX(a)或INDEX(b)都无效,必须是INDEX(a, b) - WHERE 条件字段要前置到联合索引里:比如
WHERE status = 'active' GROUP BY user_id,索引该建(status, user_id),而不是(user_id, status) -
key_len要对得上:如果字段是VARCHAR(255)但只用了前 10 字节,key_len应该是 31(utf8mb4 下 3 字节/字符 × 10 + 1),若显示 765,说明索引定义和实际查询不匹配
为什么加了索引还是慢:GROUP BY 字段被函数包裹
GROUP BY DATE(create_time)、GROUP BY UPPER(name) 这类写法会让索引完全失效——数据库没法用 B+ 树索引去匹配函数结果,只能全表扫描后逐行计算再分组。
- 替代方案:加冗余字段,比如
create_date DATE,并建索引,查询改写为WHERE create_date BETWEEN '2024-01-01' AND '2024-12-31' GROUP BY create_date - MySQL 8.0+ 可建函数索引:
CREATE INDEX idx_create_date ON t ((DATE(create_time))),但注意该语法仅限 MySQL,PostgreSQL 需用表达式索引 - 高基数字段(如 UUID、手机号)直接分组极易触发磁盘临时表,可先归类:IP → 地区码,邮箱 → 域名,再分组
怎么让 GROUP BY 少扫数据、少算中间结果
GROUP BY 的开销和输入行数强相关。扫 1000 行分组和扫 100 万行分组,CPU 和内存消耗不是线性增长,而是指数级上升。
- WHERE 必须前置:写成
SELECT city, COUNT(*) FROM user WHERE reg_date >= '2024-01-01' GROUP BY city,而不是把过滤放到 HAVING 里 - 避免
HAVING cnt > 100这种后置过滤——它意味着先分出全部城市计数,再筛,浪费巨大 - 要 TopN 结果?直接加
LIMIT 10,MySQL 5.7+ 支持GROUP BY ... LIMIT下推优化 - 多表 JOIN 后 GROUP BY 多字段(如
GROUP BY c.name, o.status)极难走索引,优先考虑子查询预聚合:SELECT c.name, t.cnt FROM customer c JOIN (SELECT o.cust_id, COUNT(*) cnt FROM orders o GROUP BY o.cust_id) t ON c.id = t.cust_id
临时表落盘了怎么办:参数调优只是兜底手段
当 Using temporary 已经出现,且确认索引、SQL 写法都无误,才考虑调参。但这是“治标”,不是“治本”。
- MySQL 中调大
tmp_table_size和max_heap_table_size(需两者一致),让更大临时表留在内存;但别无脑设到 2G——可能挤占 buffer pool - 加
SQL_BIG_RESULT提示,告诉优化器“这结果很大”,让它倾向用磁盘临时表但跳过排序;加SQL_SMALL_RESULT则相反 - PostgreSQL 用户重点调
work_mem,但要注意:该参数是 per-operation 的,一个查询里多个 sort/group by 会各自占用一份 - 真正可持续的解法是预聚合:按天/按区域建汇总表,把实时 GROUP BY 变成简单 SELECT
最常被忽略的一点:覆盖索引不只是“能用就行”。比如 SELECT dept_id, AVG(salary) FROM emp GROUP BY dept_id,索引必须是 (dept_id, salary) 才能避免回表——少一个字段,就多一次主键查找,大数据量下延迟立刻可见。


















