PostgreSQL 18 Citus 分布式集群部署指南

在 3 台 Ubuntu 22.04 服务器上基于 PostgreSQL 18 + Citus 14.2 扩展从 0 到 1 搭建 Shared-Nothing 水平分片集群(1 协调器 + 2 工作节点),覆盖扩展源安装、预加载、挂载工作节点、分布表与引用表分片及分布式执行计划实测。目标:掌握 PostgreSQL 借助 Citus 实现横向扩展的方法。

PostgreSQLPostgreSQL数据库Citus关系型数据库OLTP2026/09/30

PostgreSQL 18 Citus 分布式集群部署指南

目标:在 3 台 Ubuntu 22.04 服务器上基于 PostgreSQL 18 + Citus 14.2 扩展从 0 到 1 搭建 Shared-Nothing 水平分片分布式集群(1 协调器 + 2 工作节点),覆盖 Citus 扩展源安装、shared_preload_libraries 预加载、协调器挂载工作节点、分布表/引用表分片、分布式查询计划与跨节点分片数据落盘实测。

环境信息

  • 操作系统:Ubuntu 22.04.5 LTS(3 台服务器)
  • 系统架构:x86_64
  • PostgreSQL 版本:18.6-1.pgdg22.04+2(PostgreSQL Community Server / PGDG 源)
  • Citus 扩展版本:14.2-1(Citus Data 官方 apt 源)
  • 集群拓扑:1 协调器 + 2 工作节点(Shared-Nothing)
    • citus-node1 10.0.0.2 —— 协调器 Coordinator
    • citus-node2 10.0.0.3 —— 工作节点 Worker 1
    • citus-node3 10.0.0.4 —— 工作节点 Worker 2
  • 数据目录:/var/lib/postgresql/18/main
  • 主配置文件:/etc/postgresql/18/main/postgresql.conf
  • 认证配置:/etc/postgresql/18/main/pg_hba.conf

💡 三台服务器处于同一内网,主机名 citus-node1/2/3 互相可解析,后续统一用主机名互访。Citus 对应用基本透明:你连接的是协调器 citus-node1,分布式查询由它自动拆分到工作节点执行并汇总结果。


一、Citus 是什么、与流复制/逻辑复制有何不同

Citus 不是独立的数据库,而是 PostgreSQL 的一个开源扩展,把 PostgreSQL 变成可横向扩展的分布式数据库:

  • Shared-Nothing 架构:数据按分片键(如主键)用一致性哈希切分成互不相交的分片,分散到多个工作节点,每个节点只存一部分数据,扩容即加节点重平衡。
  • 协调器透明路由:应用只连协调器;协调器持全局元数据(pg_dist_*),把查询下推到工作节点并行执行并汇总,对应用几乎透明(兼容 PG 协议与 SQL)。
  • 适合海量数据、高并发写入(单机瓶颈被水平分片化解)。

与 PostgreSQL 另外两种集群模式的本质区别:

维度Citus(本数据分片)流复制逻辑复制
架构Shared-Nothing 分片主备 WAL 复制发布/订阅
数据关系各节点分片互不相交(并集=全量)从库=主库完整物理副本订阅端=发布端部分/全表副本
核心价值横向扩展存储与计算高可用·读写分离·容灾跨版本·按需同步
多表关联需按分片键 co-location任意目标端自行维护
典型场景海量高并发 OLTP/OLAP 扩展基础高可用、备份跨版本升级、数据汇集

二、三台服务器安装 PostgreSQL 18 + Citus 扩展(从 0 到 1)

三台服务器步骤相同,都应装 PG 服务器与 Citus 扩展(工作节点同样需要扩展)。以下以 citus-node1 为例,另两台执行一致操作。

步骤 1. 更新源并安装前置工具

在 citus-node1 上执行:

sudo apt-get update
sudo apt-get install -y curl ca-certificates gnupg

步骤 2. 添加 PostgreSQL 官方源(PGDG)

在 citus-node1 上执行:

sudo install -d /usr/share/postgresql-common/pgdg
sudo curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc
sudo sh -c 'echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
sudo apt-get update

步骤 3. 添加 Citus Data 官方源

Citus 扩展在独立的 Citus 官方 apt 源中,用官方脚本自动添加仓库与签名密钥。在 citus-node1 上执行:

curl -fsSL https://install.citusdata.com/community/deb.sh | sudo bash

执行结果(关键行):

Installing /etc/apt/sources.list.d/citusdata_community.list... done.
Importing Citus Data Community gpg key... done.
Running apt-get update... done.

The repository is set up! You can now install packages.

验证可用扩展包(可选,在 citus-node1 上执行):

apt-cache policy postgresql-18-citus-14.2

执行结果(关键行):

postgresql-18-citus-14.2:
  Installed: (none)
  Candidate: 14.2-1

步骤 4. 安装 PostgreSQL 18 与 Citus 扩展

在 citus-node1 上执行:

sudo apt-get install -y postgresql-18 postgresql-18-citus-14.2

💡 postgresql-18-citus-14.2 把 Citus 扩展(citus.so、SQL 脚本等)装入 /usr/lib/postgresql/18/lib/。装完同样会自动 initdb 初始化 18/main 集群。

步骤 5. 配置 shared_preload_libraries 预加载 citus

Citus 要求把扩展加入 shared_preload_libraries(需重启生效)。编辑 /etc/postgresql/18/main/postgresql.conf,三台都执行:

sudo sed -i "s/^#shared_preload_libraries.*/shared_preload_libraries = 'citus'/" /etc/postgresql/18/main/postgresql.conf
# 若文件尚未存在该行,则追加
echo "shared_preload_libraries = 'citus'" | sudo tee -a /etc/postgresql/18/main/postgresql.conf

放行内网互信(追加到 pg_hba.conf,三台都执行):

sudo tee -a /etc/postgresql/18/main/pg_hba.conf <<'EOF'
# Citus cluster peer net
host all all 10.0.0.0/16 trust
host all all 127.0.0.1/32  trust
EOF

启动并验证预加载(citus-node1/2/3 各自执行):

sudo pg_ctlcluster 18 main start
pg_lsclusters
sudo -u postgres psql -tAc "SHOW shared_preload_libraries;"

执行结果(三台一致):

Ver Cluster Port Status Owner    Data directory               Log file
18  main    5432 online postgres /var/lib/postgresql/18/main  /var/log/postgresql/postgresql-18-main.log

 citus

步骤 6. 三节点创建 Citus 扩展

Citus 集群里,协调器与每个工作节点都要在目标数据库创建扩展。在 citus-node1 / citus-node2 / citus-node3 各自执行:

sudo -u postgres psql -c "CREATE EXTENSION citus;"

验证版(任选一节点执行):

sudo -u postgres psql -c "SELECT extname, extversion FROM pg_extension WHERE extname = 'citus';"

执行结果:

CREATE EXTENSION

  extname | extversion
 ---------+------------
  citus   | 14.2-1
(1 row)

三、协调器挂载工作节点,组成分布式集群

只需在协调器上执行,把两个工作节点注册进集群元数据。

步骤 7. 添加工作节点

在 citus-node1 上执行:

sudo -u postgres psql -c "SELECT * FROM citus_add_node('citus-node2', 5432);"
sudo -u postgres psql -c "SELECT * FROM citus_add_node('citus-node3', 5432);"

执行结果:

 citus_add_node
----------------
              1
(1 row)

 citus_add_node
----------------
              2
(1 row)

💡 每次返回的新 groupid(1、2)即每个工作节点的内部编号,协调器据此管理分片归属。

步骤 8. 确认活动工作节点

在 citus-node1 上执行:

sudo -u postgres psql -c "SELECT * FROM citus_get_active_worker_nodes();"

执行结果:

  node_name  | node_port
-------------+-----------
 citus-node3 |      5432
 citus-node2 |      5432
(2 rows)

两个工作节点已注册可见,分布式集群成型。


四、分布表:水平分片实测

步骤 9. 设置分片数与副本数,创建分布表

在 citus-node1 上执行(设置在分布式表元数据所在的数据库,即默认 postgres 库):

sudo -u postgres psql -d postgres -c "SET citus.shard_count = 4; SET citus.shard_replication_factor = 1;"
sudo -u postgres psql -d postgres -c "CREATE TABLE orders (id bigint primary key, customer text, amount numeric(10,2), created timestamptz default now());"

按 id 做哈希分片,把表变成分布式表(在 citus-node1 上执行):

sudo -u postgres psql -d postgres -c "SELECT create_distributed_table('orders', 'id', 'hash');"

执行结果:

 create_distributed_table
--------------------------
 
(1 row)

💡 shard_count=4 把表切成 4 个分片,平均分布到 2 个工作节点;shard_replication_factor=1 每分片 1 份副本(无冗余)。Citus 会先在协调器建普通表,再按分片键把行路由到不同工作节点的分片表 orders_<id>。

步骤 10. 写入数据并验证协调器汇总

在 citus-node1 上执行:

sudo -u postgres psql -d postgres -c "INSERT INTO orders VALUES (1,'alice',100),(2,'bob',250.5),(3,'carol',79.9),(4,'dave',1200),(5,'eve',33.33),(6,'frank',66.6),(7,'grace',77.7),(8,'heidi',88.8);"
sudo -u postgres psql -d postgres -c "SELECT count(*) FROM orders;"

执行结果:

INSERT 0 8

 count
-------
     8
(1 row)

💡 应用只连接协调器,count(*) 会被 Citus 下推到每个分片并行统计再汇总,返回 8。

步骤 11. 查看分片在工作节点的分布(Shared-Nothing 证据)

在 citus-node1 上执行,查看 4 个分片各自落在哪个工作节点:

sudo -u postgres psql -d postgres -c "SELECT s.shardid, n.nodename AS worker, n.groupid FROM pg_dist_shard s JOIN pg_dist_placement p ON p.shardid=s.shardid AND p.shardstate=1 JOIN pg_dist_node n USING (groupid) WHERE s.logicalrelid='orders'::regclass ORDER BY s.shardid;"

执行结果:

 shardid |   worker    | groupid
---------+-------------+---------
  102008 | citus-node2 |       1
  102009 | citus-node3 |       2
  102010 | citus-node2 |       1
  102011 | citus-node3 |       2
(4 rows)

4 个分片两两分配到 2 个工作节点,无冗余(每分片仅 1 份)——数据被切分而非复制,这就是 Shared-Nothing 水平分片。

步骤 12. 到工作节点直接验证分片表与行数

分片表是物理存在于各工作节点 postgres 库中的真实表。citus-node2 上查看并计数:

sudo -u postgres psql -d postgres -tAc "SELECT count(*) FROM orders_102008;"
sudo -u postgres psql -d postgres -tAc "SELECT count(*) FROM orders_102010;"

执行结果(citus-node2):

3
1

citus-node3 上执行:

sudo -u postgres psql -d postgres -tAc "SELECT count(*) FROM orders_102009;"
sudo -u postgres psql -d postgres -tAc "SELECT count(*) FROM orders_102011;"

执行结果(citus-node3):

3
1

分片行数 3+1(node2)+3+1(node3)= 8,与协调器总数一致——数据按哈希均摊到各 worker,且被路由写入的不是同一份副本。

步骤 13. 查看分布式执行计划

在 citus-node1 上执行,观察协调器如何拆分任务:

sudo -u postgres psql -d postgres -c "EXPLAIN (COSTS OFF) SELECT count(*) FROM orders;"

执行结果:

                                 QUERY PLAN
----------------------------------------------------------------
 Aggregate
   ->  Custom Scan (Citus Adaptive)
         Task Count: 4
         Tasks Shown: One of 4
         ->  Task
               Node: host=citus-node2 port=5432 dbname=postgres
               ->  Aggregate
                     ->  Seq Scan on orders_102008 orders
(8 rows)

💡 Citus 把一条 SQL 拆成 4 个 Task 下推给各工作节点的分片(如上把对 orders_102008 的聚合推到 citus-node2),再在协调器汇总——这就是分布式并行执行。


五、引用表(Reference Table):全库广播复制

引用表是另一类 Citus 表:整张表在每个工作节点上各存一份完整副本,适合与分布表按分片键无关的小维表做关联,避免跨节点数据迁移。

步骤 14. 创建引用表

在 citus-node1 上执行:

sudo -u postgres psql -d postgres -c "CREATE TABLE regions (region_id int primary key, region text not null);"
sudo -u postgres psql -d postgres -c "INSERT INTO regions VALUES (1,'East'),(2,'West');"
sudo -u postgres psql -d postgres -c "SELECT create_reference_table('regions');"

执行结果:

NOTICE:  Copying data from local table...
NOTICE:  copying the data has completed
 create_reference_table
------------------------
 
(1 row)

步骤 15. 确认引用表分片广播到全部工作节点

在 citus-node1 上执行:

sudo -u postgres psql -d postgres -c "SELECT s.shardid, n.nodename FROM pg_dist_shard s JOIN pg_dist_placement p ON p.shardid=s.shardid AND p.shardstate=1 JOIN pg_dist_node n USING (groupid) WHERE s.logicalrelid='regions'::regclass ORDER BY s.shardid;"

执行结果:

 shardid |   worker
---------+-------------
  102012 | citus-node2
  102012 | citus-node3
(2 rows)

💡 同一 shardid 102012 同时出现在 citus-node2 和 citus-node3——这就是引用表"广播复制到每个工作节点"的特征(分布表则每个分片只落在单一 worker)。


六、常见问题

Q1:create_distributed_table 报 "function does not exist"

原因:目标数据库未创建 citus 扩展(CREATE DATABASE 不会被 Citus 传播到其他节点)。解决:在要做分布表的数据库上先 CREATE EXTENSION citus;,再建分布表;协调器与工作节点都要在该库建扩展。

Q2:加了节点后数据没自动均衡

新节点的数据不自动迁移。用 SELECT rebalance_table_shards(); 手工触发分片重平衡;分布表可通过 alter_distributed_table 调整分片数/副本数(shard_count、shard_replication_factor)。

Q3:分布表能 join 普通大表吗

跨节点 join 代价高。最佳实践:大表都用同一个分片键(同列)建立分布表实现 co-location(同置),小维表用引用表广播,让 join 都在本地完成。

Q4:Citus 需要 3 个节点吗

最少 1 协调器 + 1 工作节点即可运行;此处 2 个工作节点用于演示分片分布与故障冗余扩展。更多数据/更大并发加节点后用 rebalance_table_shards() 扩容。

Q5:Citus 有高可用/副本吗

有。citus.shard_replication_factor 设为 2 可让每个分片保留 2 份副本(分散在不同节点);更完整的 HA 通常叠加在 Citus 之上,工作节点本身再做流复制,或配合 PostgreSQL 分布式高可用方案。


七、参考链接