group by 高并发变慢主因常是索引不当:需确保分组字段为复合索引最左前缀,函数分组须用函数索引,类型需严格一致;临时表爆炸应先优化 where 剪枝而非调配置;高并发百万级数据宜用物化视图或汇总表;order by 与 group by 字段不一致时索引易失效,宜分层查询或反范式设计。

GROUP BY 在高并发下变慢,是不是索引没建对?
不是所有 GROUP BY 慢都怪索引,但绝大多数时候,它确实是第一排查点。MySQL 8.0+ 或 PostgreSQL 中,如果 GROUP BY 字段没走索引,优化器大概率会回表 + 文件排序(Using filesort),并发一上来,磁盘 I/O 和临时文件争抢就直接拖垮响应。
- 确保
GROUP BY的字段组合,是某个复合索引的最左前缀(比如按status, created_at分组,索引应为(status, created_at),而非单独建status索引) - 如果分组字段含函数(如
DATE(created_at)),普通索引无效,得用函数索引(MySQL 8.0+ 支持CREATE INDEX idx_date ON t ((DATE(created_at)))) - PostgreSQL 注意:
GROUP BY字段类型要和索引字段严格一致,text和varchar混用可能让索引失效
临时表撑爆 tmpdir,是该调大配置还是换写法?
别急着改 tmp_table_size 或 max_heap_table_size。临时表爆炸,往往是因为查询本身没剪枝,把几百万行先捞出来再分组——哪怕加了索引,也可能因 WHERE 条件太宽或没覆盖全部分组字段,导致无法在索引内完成聚合。
- 先用
EXPLAIN FORMAT=JSON看used_filesort和used_temporary_table是否为 true;如果是,检查WHERE是否能更早过滤数据(比如加时间范围、状态限制) - 避免在
GROUP BY后用HAVING做重量级过滤(如子查询、函数计算),尽量把条件前移到WHERE - MySQL 中,若临时结果集超过内存阈值,会自动落盘到
tmpdir,而并发高时多个查询同时落盘,IO 成瓶颈;PostgreSQL 则默认走磁盘临时表,更需警惕
用物化视图 or 汇总表替代实时 GROUP BY,什么时候划得来?
实时分组统计在并发 > 50 QPS 且数据量 > 百万行时,基本不该裸跑 GROUP BY。物化视图(PostgreSQL)或定时汇总表(MySQL)不是“高级技巧”,而是生产环境的常规减负手段。
- PostgreSQL 可用
CREATE MATERIALIZED VIEW mv_daily_stats AS SELECT date(created_at), status, count(*) FROM orders WHERE created_at >= CURRENT_DATE - INTERVAL '7 days' GROUP BY 1,2,然后定期REFRESH MATERIALIZED VIEW CONCURRENTLY - MySQL 没原生物化视图,用定时任务(
EVENT或外部调度)写入汇总表,关键是要带更新标记(如last_updated)和幂等写法(INSERT ... ON DUPLICATE KEY UPDATE) - 注意:汇总粒度要匹配业务查询习惯(比如前端只看“今日各状态订单数”,就别按小时存;但如果报表要钻取到小时,则必须保留小时级粒度)
ORDER BY + LIMIT 和 GROUP BY 一起用,为什么经常跳过索引?
这是个经典陷阱:GROUP BY a ORDER BY b LIMIT 10,即使 a 和 b 都有索引,MySQL 也大概率放弃索引,因为分组和排序字段不一致,无法复用同一棵 B+ 树完成两件事。
- 最稳妥的做法是:先分组聚合到临时结果(可借助 CTE 或子查询),再对外层结果
ORDER BY+LIMIT - PostgreSQL 更激进些,有时能用
BitmapAnd同时利用两个索引,但前提是WHERE条件足够强,能把数据量压到几千行以内 - 如果必须高频执行这类查询,考虑反范式设计:在汇总表里直接存好排序所需字段(如把
count(*)和avg(amount)都算好),避免运行时计算
临时表不是缓存,物化视图不是银弹,索引也不是建了就灵——真正卡住性能的,往往是那条没被注意到的 WHERE 条件,或者那个以为“应该能走索引”的函数调用。










