该用 citus_table_type = ‘distributed’ 当主业务表数据量大(>百万行)、有高基数分布列(如 user_id)、读写频繁;用 ‘reference’ 仅当表小(

什么时候该用 citus_table_type = 'distributed' 而不是 'reference'
分布式表是 Citus 水平拆分数据的默认方式,适用于写多、读多、数据量大且能按某列(distribution_column)合理切分的场景。参考表则把全量副本存到每个 worker 节点,适合小而稳、频繁 JOIN 的维表(比如 countries 或 currency_codes)。
常见错误现象:Error: cannot execute INSERT on distributed table "orders" because distribution column "user_id" is NULL —— 这说明你设了分布键但插入时没提供值,或者用了 NULL;而参考表不会报这个错,但它会在每个节点复制整张表,插入 1 行 = 写 N 个节点。
- 选分布式表:主业务表(如
orders、events),行数 > 百万,有明确高基数分布列(如tenant_id、user_id) - 选参考表:小于 10MB、更新极少、被多个分布式表高频 JOIN 的表(如
product_categories) - 别误用参考表替代分区逻辑:它不解决单表膨胀,只解决广播 JOIN 开销;一旦表变大或更新频繁,会拖慢所有 DML
citus_create_reference_table() 和 citus_create_distributed_table() 的参数差异
两个函数看着像兄弟,但行为完全不同:citus_create_reference_table() 只接受表名,不支持分布键或分片数;citus_create_distributed_table() 必须指定 distribution_column,还能选 colocate_with 和 shard_count。
容易踩的坑:citus_create_reference_table('users') 成功后,再对 users 执行 citus_create_distributed_table() 会报错 ERROR: relation "users" is already a citus table —— Citus 不允许二次转换类型,得先 select citus_drop_all_shards('users') 清理元数据(注意:这不删数据,但需手动 DROP TABLE)。
-
citus_create_reference_table():无参数可选,隐式使用replication_factor = number_of_workers -
citus_create_distributed_table():distribution_column必填;colocate_with建议显式指定,否则可能触发默认 co-location group 创建,导致后续 JOIN 无法下推 - 如果漏写
colocate_with,Citus 会建新 group,哪怕你两表都按tenant_id分布,JOIN 仍走 coordinator 汇总,性能断崖下跌
co-location group 不匹配导致 JOIN 变慢的真实表现
当你看到 EXPLAIN 输出里出现 Remote Subquery Scan on worker_1, worker_2 + 大量 Hash Join 在 coordinator 上执行,基本就是 co-location 失效了。Citus 只有在两表分布键相同、分片数一致、且属于同一 co-location group 时,才把 JOIN 下推到 worker 节点本地执行。
典型场景:先建了 orders(按 tenant_id 分布),再建 order_items 却没指定 colocate_with => 'orders',即使你也用 tenant_id 作分布键,Citus 也会新建 group,JOIN 就回退成 coordinator 收集+合并模式。
- 检查 co-location:
SELECT table_name, colocation_id FROM citus_tables WHERE table_name IN ('orders', 'order_items');—— ID 不同即未共置 - 修复方法不是 ALTER,而是重建:
DROP TABLE order_items; CREATE TABLE ...; SELECT citus_create_distributed_table('order_items', 'tenant_id', colocate_with => 'orders'); - 分片数差异也会破坏 co-location:比如
orders用默认shard_count = 32,而order_items显式设为shard_count = 16,即使colocate_with正确,group ID 也不同
本地表(non-Citus 表)在 Citus 集群里的实际定位
本地表就是普通 postgresql 表,Citus 完全不管理它:不分片、不复制、不参与任何分布式查询优化。它只存在于 coordinator 节点,worker 上没有数据也没有元数据记录。
使用场景很窄:临时中间结果(CREATE TEMP TABLE)、纯 coordinator 侧配置表(如 app_settings)、或尚未迁移的老系统表。一旦你在 JOIN 中混用本地表和分布式表,整个查询会强制上推到 coordinator 执行,哪怕只查 1 行。
- 不能被
citus_add_local_table_to_metadata()“骗”进分布式体系——它只是让 Citus 认出这张表存在,但依然不参与分片或 co-location - 想让它参与 JOIN 下推?不行。唯一办法是转成参考表(如果够小)或分布式表(如果可分片)
- 备份恢复时容易漏掉:pg_dump 默认不导出本地表,除非显式加
--include-foreign-tables或单独 dump coordinator 数据库
co-location 不是开关,是依赖关系网;一个表的分布策略选错,可能让周边五张表的 JOIN 全部降级。最常被忽略的是分片数隐式继承和参考表的写放大问题——它们不会立刻报错,但会在数据增长到几十 GB 后突然拖垮集群。