直接GROUP BY大表会卡住,因需将所有匹配行加载至内存或磁盘临时表进行哈希分组或排序,行数越多,CPU、内存、I/O消耗剧增;无索引时更触发Using temporary; Using filesort。

为什么直接 GROUP BY 大表会卡住?
因为数据库执行 GROUP BY 时,必须把所有匹配的行拉到内存(或磁盘临时表)里做哈希分组或排序,输入行数越多,CPU、内存、I/O 消耗越不成比例地飙升。2000 万行的表,WHERE 后剩 500 万,GROUP BY 就得处理这 500 万;如果没索引,还会触发 Using temporary; Using filesort——这是性能断崖的典型信号。
分片统计 + 全局汇总的关键实操点
这不是“拆 SQL”,而是用可控的、可并行的子任务替代单点重压。核心是让每个分片能独立完成局部聚合,再合并结果。常见落地方式有:
- 按时间/区域等业务维度切分:比如日志表按
dt字段分区,查“近 30 天各城市 UV”,就分别跑SELECT city, COUNT(DISTINCT uid) FROM log WHERE dt = '2026-05-01' GROUP BY city… 直到 5 月 18 日,最后在应用层或临时表里UNION ALL+ 再GROUP BY city - 用主键范围分片:对无天然业务维度的表(如用户关系表),按
id BETWEEN ? AND ?切成若干段,每段走自己的GROUP BY,注意避免漏数据或重复(边界要闭合,且用id >= ? AND id 更安全) - 避免在分片阶段用高开销函数:比如不要在每个分片里都算
MD5(phone)再分组,而应提前把归一化字段(如city_code)建好并索引,否则分片只是把瓶颈从全局挪到每个子查询 - 汇总层慎用
COUNT(DISTINCT):分片后全局去重无法简单加总。若必须,可用 HyperLogLog 类似算法(PostgreSQL 的HLL扩展、MySQL 8.0+ 的APPROX_COUNT_DISTINCT),或改用布隆过滤器在应用层合并
什么时候该停手,换物化方案?
如果你发现同一类 GROUP BY 查询每周跑 50+ 次,且容忍 T+1 延迟,那就别再拼分片脚本了。直接建汇总表:
- 每天凌晨跑一次
INSERT INTO daily_city_stats SELECT city, COUNT(*), COUNT(DISTINCT uid) FROM log WHERE dt = '2026-05-17' GROUP BY city - 给汇总表建主键
(dt, city),查询时WHERE dt BETWEEN ... AND ...能走索引范围扫描,响应稳定在毫秒级 - MySQL 不支持原生物化视图,但可用事件调度器(
EVENT)自动刷新;PostgreSQL 可直接用CREATE MATERIALIZED VIEW+ 定期REFRESH
分片统计是“临时止痛”,物化是“手术根治”。真正容易被忽略的是:一旦上了物化路径,就得同步维护一致性——比如源表发生 DELETE,汇总表是否及时减掉?这点常被跳过,导致统计数据悄悄漂移。


















