分布式group by不能直接执行,因标准sql缺乏跨服务器协调能力,需显式拆分为local+global两阶段聚合,且分组键须与分片键一致、where可下推、禁用having/窗口函数等破坏局部性的操作。

分布式GROUP BY为什么不能直接写SELECT … GROUP BY …
因为标准SQL没有跨服务器执行计划协调能力。你写的GROUP BY语句在单库里能跑,在跨多个物理数据库实例的场景下,数据库引擎根本不知道另一台机器上有哪些分组键、哪些中间聚合结果——它连连接都还没建好。强行执行只会触发全量拉取(把所有原始数据从各节点传到协调节点再分组),通信开销爆炸,超时或OOM是常态。
必须显式拆成LOCAL + GLOBAL两阶段聚合
真正可行的路径只有一条:先让每个分片节点自己完成局部聚合,再把轻量级中间结果汇总到协调节点做全局合并。但这个流程不会自动发生,必须靠人工干预引导:
- 分组字段(如
user_id、product_id)必须和分片键一致,否则节点无法判断“该不该算我这组” - WHERE条件要能下推,比如
WHERE shard_key % 4 = 0或WHERE region = 'cn-east',让节点只处理归属自己的数据 - 外层查询必须包裹子查询,且子查询中不能出现未聚合的非分组列(否则协调器会放弃局部优化)
- StarRocks/Doris等MPP系统需确认
enable_local_shuffle_agg=1或new_planner_agg_stage=2已开启
示例(StarRocks):
SELECT product_id, SUM(total_cnt) AS cnt FROM ( SELECT product_id, COUNT(*) AS total_cnt FROM distributed_sales WHERE dt = '2026-09-28' -- 下推过滤 GROUP BY product_id -- 局部聚合,依赖product_id是分桶键 ) t GROUP BY product_id; -- 全局合并
HAVING和窗口函数会让局部聚合彻底失效
这是最容易被忽略的断点。HAVING COUNT(*) > 100或ROW_NUMBER() OVER (PARTITION BY category)这类逻辑,必须看到全部分组结果才能判断,节点本地没能力决定“这一组要不要保留”。一旦出现,优化器会直接退化为单节点全量聚合。
应对方式很实在:
- 把
HAVING条件尽量前移到WHERE:比如用WHERE status = 'paid'替代HAVING COUNT(*) > 0 - 窗口函数若必须用,确保
PARTITION BY字段和分片键完全一致,否则大概率触发广播重分布 -
COUNT(DISTINCT)天然不支持局部合并,考虑改用APPROX_COUNT_DISTINCT或预计算布隆过滤器
云数据库上别信“自动优化”,务必用EXPLAIN看真实执行计划
阿里云RDS、腾讯云CDB、AWS RDS这些托管服务,表面宣称支持分布式查询,实际执行时仍可能因配置、版本、参数差异跳过局部聚合。关键动作只有一个:EXPLAIN输出里必须同时出现AGGREGATE (LOCAL)和AGGREGATE (GLOBAL)两个节点。
如果只看到一个AGGREGATE,或者出现Using temporary、Using filesort,说明你写的SQL已被降级为单机模式——此时再怎么调索引也没用,得回头检查分片键对齐、下推条件、聚合表达式是否合规。










