SQL Citus 的分布式表 vs 参考表 vs 本地表类型与 co-location 优化

1次阅读

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

SQL Citus 的分布式表 vs 参考表 vs 本地表类型与 co-location 优化

什么时候该用 citus_table_type = 'distributed' 而不是 'reference'

分布式表是 Citus 水平拆分数据的默认方式,适用于写多、读多、数据量大且能按某列(distribution_column)合理切分的场景。参考表则把全量副本存到每个 worker 节点,适合小而稳、频繁 JOIN 的维表(比如 countriescurrency_codes)。

常见错误现象:Error: cannot execute INSERT on distributed table "orders" because distribution column "user_id" is NULL —— 这说明你设了分布键但插入时没提供值,或者用了 NULL;而参考表不会报这个错,但它会在每个节点复制整张表,插入 1 行 = 写 N 个节点。

  • 选分布式表:主业务表(如 ordersevents),行数 > 百万,有明确高基数分布列(如 tenant_iduser_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_withshard_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 后突然拖垮集群。

text=ZqhQzanResources