citus要求join表的distribution_column必须完全一致才能高效执行本地join,否则触发跨节点shuffle或广播join,性能下降5倍以上;需确保列名、类型、colocation_id三者严格匹配,并通过pg_dist_partition和pg_dist_colocation验证。

为什么 citus_table_distribution_column 必须一致才能高效 join
因为 Citus 在执行分布式 join 时,默认只对「分布列值相同」的分片做本地 join,否则就要跨节点 shuffle 数据——这会拖慢 5 倍以上。你查 pg_dist_partition 就能看到,只要两个表的 distribution_column 不同或类型不兼容,Citus 就自动退化为广播 join 或重分布 join。
- 必须确保参与 join 的所有表都用同一列做分布,比如都是
tenant_id,且类型完全一致(Integer和bigint算不一致) - 如果业务上天然没有公共分布键(比如订单表按
order_id分布,用户表按user_id分布),那就得加冗余字段或改分布策略,硬连会触发Error: cannot perform distributed join on non-co-located tables -
CREATE TABLE ... DISTRIBUTED BY (tenant_id)是显式声明,别依赖默认行为;建完用select * FROM citus_shards WHERE table_name = 'orders'核对实际分布列
如何验证两张表是否真正 co-located
光看建表语句不够,Citus 的 co-location 判断基于 colocation_id,不是名字或结构。同一个 colocation_id 才代表它们被当成一组调度,否则即使分布列名一样也会被拆开。
- 查
pg_dist_colocation和pg_dist_partition:SELECT p1.logicalrelid, p1.partkey AS dist_col, p1.colocationid FROM pg_dist_partition p1 WHERE p1.logicalrelid IN ('orders', 'customers'); - 如果两行
colocationid不同,说明没 co-locate,哪怕分布列都是tenant_id—— 这通常是因为建表时没指定COLOCATE WITH - 补救方法是用
SELECT citus_add_local_table_to_metadata('orders')+SELECT citus_add_table_to_metadata('customers', 'tenant_id', colocation_id => X),但注意:已有数据不会自动重分布,得手动ALTER TABLE ... SET DISTRIBUTED BY并重写
citus.enable_repartition_joins = off 为什么不能乱开
这个 GUC 开启后,Citus 会在 join 时自动把非 co-located 表重分布到临时分片,听起来很智能,但代价极高:它会把整个右表复制到每个 worker 节点,内存和网络压力陡增,尤其右表超 1GB 时几乎卡死。
- 仅在 ad-hoc 分析、小表关联、且明确知道右表
- 生产环境应设为
off(默认值),靠建模解决 co-location,而不是靠参数兜底 - 开启后若出现
could not connect to server: Connection refused或out of memory,大概率是 repartition 导致 worker OOM,不是网络问题
JOIN 中带 WHERE 条件却依然慢?检查 Filter pushdown 是否生效
Citus 能把 WHERE 条件下推到分片级执行,但前提是条件字段必须是分布列,或者该表是引用表(reference table)。否则,它会先拉全量数据再过滤,join 性能直接崩盘。
- 比如
SELECT * FROM orders JOIN customers using (tenant_id) WHERE orders.created_at > '2024-01-01':如果created_at不是分布列,Citus 就没法下推,每个分片都会扫全部 orders 数据 - 用
EXPLAIN (VERBOSE)看执行计划里有没有Remote Subplan包含你的 WHERE 条件;没有就说明没下推 - 解决办法只有两个:要么把查询条件改到分布列上(如
WHERE tenant_id IN (1,2,3)),要么给大表加 partial index(如CREATE INDEX ON orders (tenant_id) WHERE created_at > '2024-01-01'),但后者只对单表有效,join 时仍受限
co-location 不是建表时点个选项就完事的事,它要求分布列语义一致、类型严格匹配、colocation_id 显式对齐,而且一旦数据量上来,任何偏差都会在慢查询里立刻暴露。