PARTITION BY本身不等于局部聚合,因其仅定义窗口计算范围且不减少行数;真正的局部聚合需依赖分布式引擎对GROUP BY的两阶段优化(如分片键匹配、enable_local_shuffle_agg开启等),并须通过EXPLAIN验证AGGREGATE (LOCAL)节点存在。

为什么 PARTITION BY 本身不等于局部聚合
PARTITION BY 是窗口函数的分组机制,它只控制计算范围,不减少数据量,也不触发物理层面的数据下推或分片计算。真正的“局部聚合”是分布式数据库(如 StarRocks、Doris、SQL Server PDW)在执行计划中把 GROUP BY 拆成两阶段:先在每个节点上按分片键做一次 GROUP BY(局部),再把中间结果发给协调节点汇总(全局)。PARTITION BY 窗口函数跑在哪一层、是否下推,完全取决于引擎是否识别出可优化路径——它自己不会自动触发局部聚合。
哪些写法能实际触发局部聚合
关键不是用了 PARTITION BY,而是让优化器确信:聚合可以安全地拆到各节点本地完成。满足以下条件才可能生效:
- 分组字段必须和表的分片/分桶键一致,例如表按
user_id哈希分片,那么GROUP BY user_id才可能下推;写成GROUP BY region就大概率退化为单节点处理 - 避免在
SELECT中引用未参与分组的非聚合列,比如SELECT user_id, name, COUNT(*) FROM t GROUP BY user_id会强制拉取全量原始行,破坏局部性 - 不要在外层加
ORDER BY或LIMIT,它们常导致执行计划跳过局部阶段;StarRocks 需确认enable_local_shuffle_agg = true,Doris 要检查new_planner_agg_stage = 2 - 用
EXPLAIN查看执行计划,必须看到类似AGGREGATE (LOCAL)和AGGREGATE (GLOBAL)两个节点,而不是只有后者
PARTITION BY 窗口函数怎么配合局部聚合用
它不直接参与局部聚合,但能作为“后处理”补充:先用真正 GROUP BY 完成局部+全局两阶段聚合,再用窗口函数在聚合结果上算占比、累计值等。常见组合场景:
- 算分组内占比:内层
GROUP BY category得到各组cnt,外层用SUM(cnt) OVER (PARTITION BY category_type)算大类下的子类分布 - 避免在窗口里嵌套
GROUP BY:像COUNT(*) OVER (PARTITION BY x) / COUNT(*) OVER ()这种写法,在分布式环境下分母仍是全量扫描,无法利用局部聚合优势 - 如果必须用
STRING_AGG或ROW_NUMBER(),确保PARTITION BY字段与分片键一致,否则可能引发跨节点重分布,抵消局部聚合收益
最容易被忽略的破坏点
两个看似无害的操作,会让局部聚合彻底失效:
-
HAVING子句:一旦写HAVING COUNT(*) > 100,协调节点无法预判哪些组满足条件,只能把所有局部结果全量上拉再过滤——局部阶段白做了 - 窗口函数带
ORDER BY且不匹配分片键:比如按create_time排序的ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY create_time),若create_time不是分片依据,各节点排序结果无法合并,引擎会放弃局部聚合,改走广播或重分区
局部聚合不是开关,是执行计划对语义的推断结果;写 SQL 时得时刻想着:这个分组,能不能在每个节点上独立做完?做完之后,中间结果还够不够小、够不够结构化,让上层能继续安全合并?











