GROUP BY越大数据越慢的根本原因是数据量增大后触发的排序、临时表和全表扫描连锁恶化;MySQL执行时需先扫描数据、隐式排序、再分组聚合,一旦内存不足就落盘,导致性能断崖下跌。

GROUP BY 为什么越大数据越慢
根本原因不是 GROUP BY 本身“变慢”,而是它触发的底层操作在数据量增大后迅速失控:排序、临时表、全表扫描这三者会连锁恶化。
MySQL 执行 GROUP BY 时实际干了什么
你写 GROUP BY category,MySQL 并不会直接分组。它通常要走这几步:
- 先读取所有满足
WHERE条件的行(若无索引,就是全表扫描) - 把结果按
category排序(哪怕你没写ORDER BY,GROUP BY 默认隐式排序) - 排序后相邻相同值自然成组,再算
COUNT(*)或SUM(amount) - 如果内存不够放全部排序数据,就写磁盘临时表——这时性能断崖下跌
关键点:Using temporary; Using filesort 出现在 EXPLAIN 输出里,就是这个流程已失控的明确信号。
索引为什么不能总起作用
索引对 GROUP BY 的加速效果高度依赖字段顺序和查询结构:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
-
CREATE INDEX idx_cat ON transactions(category)只对纯GROUP BY category有效;一旦加了WHERE amount > 100,这个单列索引大概率被忽略 - 复合索引必须遵循最左前缀:想支持
WHERE status = 'paid' GROUP BY category,得建idx_status_category(status, category),反过来就不行 - 如果 SELECT 中还有非索引字段(比如
SELECT category, MAX(note) FROM ...),MySQL 仍需回表,索引优势被抵消
更隐蔽的坑:GROUP BY category, created_at 这种多字段分组,几乎不可能用单个索引完全覆盖,临时表概率陡增。
tmp_table_size 和 max_heap_table_size 真的有用吗
调大这两个值能缓解问题,但只是“止痛”,不是根治:
- 设为 256M 可能让原本写磁盘的临时表留在内存里,查询从 45 秒降到 8 秒——但这只对当前查询有效
- 所有并发查询都可能争抢这块内存,全局调高反而引发 OOM 或锁等待
- 真正该做的,是让查询根本不需要临时表:靠覆盖索引(
SELECT category, COUNT(*) FROM t GROUP BY category+INDEX(category))或提前过滤(WHERE created_at >= '2026-05-01')
最容易被忽略的是:GROUP BY 的性能拐点往往不在千万级,而在索引失效+并发叠加的那一刻——这时候看执行计划比调参数管用十倍。










