在构建基于 PostgreSQL 的分布式数据库系统时,Citus 通过扩展插件的方式把单机数据库变成可水平扩展的集群。它并不是把所有表都一律打散到各个节点,而是提供了分布式表、参考表和本地表三种不同的表类型。理解这三种类型的适用场景,以及如何使用 co-location(共置)优化关联查询,是设计高性能 Citus 集群的核心。

一、Citus 的三种表类型
Citus 的表类型决定了数据在集群中的分布方式。选择错误的表类型,可能导致写入热点、查询跨节点广播或无法利用本地计算能力。下面分别说明每种类型的机制与典型用法。
分布式表(Distributed Table)是 Citus 最核心的表类型。创建分布式表时需要指定一个分布键(distribution column),Citus 会根据该列的哈希值将行映射到不同的分片(shard),每个分片再放置到不同的工作节点上。这种表适合数据量大、需要横向扩展写入和存储的场景,例如订单表、日志表、用户行为表等。
分布式表的优势在于可以随节点增加近乎线性地扩展吞吐。但它的约束也很明显:如果查询没有带上分布键,Citus 往往需要把请求广播到所有分片,再在协调器汇总结果。此外,两张分布式表做关联时,若分布键不同,就会触发跨节点数据重分布,代价较高。
参考表(Reference Table)是一种被完整复制到每一个工作节点上的表。它不按分布键分片,而是在所有节点保留全量副本。参考表非常适合体积较小、更新不频繁但被大表频繁关联的维度表,例如国家编码表、商品类目表、配置表等。
当分布式表与参考表进行连接时,由于参考表在每个节点都存在,Citus 可以直接在本地完成连接,不需要把参考表数据在网络中移动。这就避免了跨节点 shuffle,是降低延迟的重要手段。不过参考表不宜过大,否则多节点冗余会消耗大量存储与维护成本。
本地表(Local Table)是指仅存在于协调器节点、不被 Citus 分片也不被复制到工作节点的普通 PostgreSQL 表。它通常用于迁移遗留系统、存放不参与分布式查询的元信息,或作为一些仅需单节点处理的报表中间表。
本地表不会被自动下推到工作节点。如果分布式表需要和本地表关联,Citus 只能把分布式表的数据拉回协调器再连接,网络开销很大。因此本地表应尽量避免与分布式表做大规模关联,仅作为辅助存储使用。
二、co-location 共置优化原理
co-location 指的是让多个分布式表使用相同的分布键和分片数,使相同分布键值的数据落在同一个工作节点的同一个分片上。这样一来,相关表之间的连接、聚合就可以在节点内部完成,而不必跨节点搬运数据。
在没有 co-location 的情况下,假设订单表按 user_id 分片,订单明细表按 order_id 分片,两者做连接时,Citus 必须将其中一张表按另一张表的分片方式重新分布,产生大量网络传输。而如果把两张表都按 user_id 作为分布键,并放入同一个 co-location 组,同一用户的所有订单和明细都会在同一个节点,连接变成本地操作。
创建共置表时,可以在创建分布式表时指定 colocate_with 参数。以下示例展示如何建立共置的订单表与订单明细表:
-- 先创建并分片订单表,作为共置基准
SELECT create_distributed_table('orders', 'user_id');
-- 订单明细表与 orders 共置,使用相同分布键 user_id
SELECT create_distributed_table('order_items', 'user_id', colocate_with => 'orders');
-- 查询同一用户的订单与明细,可在节点内完成
SELECT o.order_id, i.item_name, i.price
FROM orders o
JOIN order_items i ON o.order_id = i.order_id
WHERE o.user_id = 123;
上述代码中,colocate_with 参数显式把 order_items 表与 orders 表放在同一共置组。由于两者都按 user_id 分布,涉及同一用户的查询不会跨节点。需要注意,共置要求分布键类型一致、分片数量一致,否则 Citus 无法保证数据同址。
除了手动指定,Citus 在默认情况下也会把相同分布键和分片数的表自动归入同一个共置组。但当业务需要跨多个独立集群或特殊拓扑时,显式声明更稳妥。共置不仅利于连接,也利于事务与外键约束的本地化校验。
三、表类型与 co-location 的选型建议
实际项目中,不应只凭数据大小决定表类型。下面用一张对照表总结关键差异:
| 表类型 | 数据分布 | 适用场景 | 关联代价 |
|---|---|---|---|
| 分布式表 | 按分布键哈希分片到各节点 | 大表、高并发写入 | 同键共置时低,异键时高 |
| 参考表 | 全量复制到所有节点 | 小维度表、配置表 | 与分布式表连接极低 |
| 本地表 | 仅协调器节点 | 遗留数据、单节点报表 | 与分布式表连接很高 |
选型时,优先把事实表建成分布式表,并把与之频繁关联的小维度表建成参考表。若事实表之间本身存在天然归属关系(如用户维度的多张表),则统一分布键并启用 co-location。本地表仅作为兼容层或管理表使用,不要承载核心查询路径。
一个常见误区是盲目把所有表都设为分布式表,结果维度表也被打散,每次报表查询都触发跨节点广播。另一种误区是把过大的表设为参考表,导致每个节点存储与维护成本飙升。合理组合三种类型,并用共置减少数据移动,才能发挥 Citus 的扩展能力。
四、验证共置与查询下推
部署后可通过 Citus 的系统视图检查表是否处于同一共置组,以及查询是否避免了重分布。以下语句列出表的分布与共置信息:
-- 查看分布式表及其共置组
SELECT logicalrelid, colocationid, distribution_column
FROM pg_dist_partition
WHERE logicalrelid IN ('orders', 'order_items');
若两条记录的 colocationid 相同,说明已共置。再配合 EXPLAIN 观察执行计划,若看到 Custom Scan (Citus Adaptive) 下没有 Repartition 或 Move 操作,即表明连接已在节点内完成。持续监控慢查询,调整分布键或参考表策略,可进一步巩固性能。
综上,Citus 的表类型并非越多越复杂越好,而是要用对地方。分布式表解决规模问题,参考表消除维度广播,本地表保留兼容灵活度,而 co-location 是把分布式表之间关联代价压到最低的关键手段。理清这几者关系,分布式 PostgreSQL 的架构设计就清晰了许多。
Citusdistributed_tableco-location修改时间:2026-08-09 00:00:57