两阶段聚合能减少网络传输,根本原因是把“全量数据搬运”变成“局部结果搬运”,即各节点先本地分组聚合(如product_id=123, local_count=47),仅传输压缩10–100倍的轻量中间结果,避免单阶段中所有原始行集中发送至中心节点导致带宽打满。

两阶段聚合为什么能减少网络传输
根本原因是把“全量数据搬运”变成“局部结果搬运”。单阶段聚合时,所有节点把原始行发到一个中心节点,再由它统一分组求和;两阶段则让每个节点先做一次GROUP BY + SUM,只把“product_id=123, local_count=47”这种轻量结果发出去,中间数据体积通常压缩 10–100 倍。
典型症状是监控看到大量 Exchange 算子、网络带宽打满但 CPU 不高——这说明瓶颈不在计算,而在搬运。两阶段正是为解这个而生。
StarRocks / Doris 的两阶段生效条件
引擎默认开启两阶段,但不等于真用上。必须同时满足:
-
GROUP BY字段必须是表的分区分桶键(如DISTRIBUTED BY (product_id)),否则无法保证相同product_id落在同一节点,局部聚合就失去意义 - 外层不能有
ORDER BY或LIMIT,否则优化器可能跳过局部阶段,直接走全局聚合 - 确认配置已启用:StarRocks 需
SET enable_local_shuffle_agg = true;Doris 需new_planner_agg_stage = 2
为什么 TiDB 很难真正下推 GROUP BY
TiDB 的执行计划里常出现 HashAgg 在最外层,意味着聚合全在 TiDB Server 层做,TiKV 只负责扫描。原因很实在:
-
WHERE条件没下推:比如写WHERE YEAR(created_at) = 2024,函数导致Selection挂在顶层,TiKV 返回全部原始行 - 分组字段无索引或类型不匹配:如
region_code是VARCHAR(32),查询传入'east'却被隐式转成TEXT,索引失效,被迫全表扫 - 用
EXPLAIN FORMAT = 'VERBOSE'查看执行计划,若GroupBy出现在TableReader或IndexScan节点内部,才算真正下推成功
容易被忽略的倾斜陷阱
即使开了两阶段,如果分组键严重倾斜(比如 80% 订单都集中在 region_code = 'east'),局部聚合后,那个节点仍要发几十 GB 到中心节点,反而比均匀分布更卡。
这不是配置问题,而是数据建模问题。此时该考虑:
- 拆分热点值:把
'east'细分为'east_01'~'east_10',用哈希或业务规则分散 - 改用预聚合表:按天/按区域提前算好汇总,
GROUP BY只查小表 - 避免在分组字段上用函数:如
GROUP BY DATE(created_at)会强制放弃索引,应改用范围查询 + 分区剪枝
两阶段不是银弹,它依赖分片逻辑、索引设计、查询写法三者咬合。漏掉任何一环,看起来开了,实际还是单点扛压。

















