group by 在亿级表上慢是因为全表扫描后需内存/磁盘排序分组并聚合,数据量越大,i/o、内存与cpu开销越不可控;索引必须为覆盖式复合索引(如dt, city, amount),且顺序需匹配查询生命周期:先过滤、再分组、最后取值;having逻辑不可写入where,高区分度条件应前置where过滤;null值导致哈希倾斜,需not null约束或coalesce归一;分区表须确保where条件触发分区裁剪,避免函数破坏裁剪。

GROUP BY 为什么在亿级表上会慢
因为默认执行路径是:全表扫描 → 内存/磁盘排序分组 → 聚合计算。数据量越大,排序和哈希分组的内存压力、I/O开销、CPU消耗就越不可控。哪怕加了索引,如果 WHERE 条件没命中、或 GROUP BY 字段不在索引最左前缀,索引也大概率被跳过。
必须建复合索引,且顺序要对
索引不是“有就行”,而是要覆盖查询生命周期:先过滤、再分组、最后取值。比如查「2024年各城市销售额」,语句是:SELECT city, SUM(amount) FROM sales WHERE dt >= '2024-01-01' GROUP BY city,对应索引应为:CREATE INDEX idx_dt_city_amount ON sales (dt, city, amount)。
-
dt放最左:确保WHERE能走索引范围扫描 -
city紧跟其后:让分组能在索引内完成,避免回表或额外排序 -
amount加入:构成覆盖索引,SUM(amount)直接从索引页读取,不查数据页
如果只建 (city) 或 (city, dt),数据库很可能放弃索引,退回到全表扫描+外部排序。
HAVING 条件别写进 WHERE,但聚合过滤要前置
常见错误是把本该用 HAVING 的逻辑硬塞进 WHERE,比如写 WHERE SUM(amount) > 10000 —— 直接报错。但反过来,如果业务允许,把高区分度过滤条件尽量往前压:
- 用
WHERE先筛掉 90% 无效数据(如状态 = 'paid'、租户 ID = 123) - 避免在
HAVING里写复杂表达式,如HAVING COUNT(*) / SUM(quantity) > 0.8;拆成子查询或预计算列更稳 - 若需按聚合结果分页(如 Top 10 城市),优先用
LIMIT+ORDER BY,而非先算全量再LIMIT
NULL 值和分区裁剪是隐形性能杀手
亿级表里,GROUP BY 字段若存在大量 NULL,会导致分组哈希桶倾斜;而未启用分区裁剪时,哪怕只查昨天的数据,也可能扫完整个历史分区。
- 对关键分组字段(如
region、tenant_id)加NOT NULL约束,或用COALESCE(region, 'unknown')统一归类,减少空值干扰 - 按时间或租户做范围/列表分区后,确认查询是否触发裁剪:执行
EXPLAIN看Partitions列是否只显示目标分区 - 避免在分区键上用函数,如
WHERE DATE(dt) = '2024-06-01'会失效;改用WHERE dt >= '2024-06-01' AND dt
真正卡住的往往不是语法,而是 NULL 分布不均、分区没生效、或者索引字段顺序错了——这些在百万级表里不显眼,在亿级表里直接拖垮响应时间。











