本文是 PostgreSQL overview 的 implementation-layer deep article。Overview 已說明 PG 在 OLTP 譜系的定位、本文聚焦 Citus distributed extension — 把 PG 變成 sharded cluster 的方式。


當 PG single-primary 寫吞吐撞上單機極限(50K-100K WPS)、選項三條:

  1. Application 層 sharding:應用層自管 shard routing
  2. Citus:PG extension、自動 routing + cross-shard query
  3. Distributed SQL(CockroachDB / Aurora DSQL / Spanner):不同 engine

選 Citus 的核心 driver:保留 PG SQL syntax + extension 生態。但「應用層幾乎不必改」是樂觀說法 — 實際上 application 必須圍繞 distribution column 重設計(query 加 filter / transaction 限定同 shard / reference table 量控制)、跟 Vitess 比 cross-shard query 自動化弱。代價是 coordinator / worker 部署複雜度 + cross-shard query 限制 + application schema 改造工作量

閱讀本文前可先對齊 Database Sharding 的 shard key、routing、resharding 與 cross-shard query 語意;容量失衡時再接 Hot Partition

MySQL Vitess sharding 的核心差異:Citus 是 PG extension(PG 自己跑)、Vitess 是 獨立 proxy + tablet 系統(包 MySQL)。Citus 用 PG 原生機制(FDW / extension hook)、Vitess 是 外部包裝

Citus 架構:Coordinator + Worker

 1                ┌─────────────────┐
 2   Application  │   Coordinator   │  ← 對外 PG wire protocol、planner、routing
 3                │   (Citus + PG)  │
 4                └────┬─────┬──────┘
 5                     │     │
 6              ┌──────┘     └──────┐
 7              ▼                   ▼
 8        ┌──────────┐         ┌──────────┐
 9        │ Worker 1 │         │ Worker 2 │  ← 各跑 PG + Citus extension
10        │  (PG)    │         │  (PG)    │
11        │ shard 1,3│         │ shard 2,4│
12        └──────────┘         └──────────┘

Coordinator

  • 對 application 看起來像 PG(同 port / 同 wire protocol)
  • 接 SQL → Citus planner 把 query 分解 + route 給 worker
  • 不存 data(distributed table 的 shard 在 worker 上)
  • metadata(哪個 shard 在哪個 worker)

Worker

  • 標準 PG instance + Citus extension
  • 各存若干 shard
  • 接 coordinator 來的 query、跑 local execute、回結果

Shard

  • Distributed table 拆成 N 個 shard(預設 32)
  • 每 shard 是 worker 上的 physical PG table(含 _<shardid> 後綴)
  • 行為跟一般 PG table 一樣、可以直接連 worker 用 PG 工具 access

3 種 Table Type

Distributed table — 跨 shard 切分

 1-- 建一般 PG table
 2CREATE TABLE orders (
 3    id BIGSERIAL,
 4    user_id BIGINT NOT NULL,
 5    amount DECIMAL(10,2),
 6    created_at TIMESTAMP,
 7    PRIMARY KEY (user_id, id)  -- PK 必須含 distribution column
 8);
 9
10-- 用 Citus 把它變 distributed
11SELECT create_distributed_table('orders', 'user_id');

user_iddistribution column — Citus 用它的 hash 決定 row 屬哪個 shard。PK 必須含 distribution column(跟 MySQL partitioning 同要求)。

跟 Vitess Vindex 對比:

  • Citus:hash distribution column → shard(單一 hash function、不可選 algorithm)
  • Vitess:Vindex 可選多種(hash / lookup_hash / xxhash / null)

Reference table — 全 shard 共有

1CREATE TABLE products (
2    id SERIAL PRIMARY KEY,
3    name VARCHAR(100),
4    price DECIMAL
5);
6
7SELECT create_reference_table('products');

products每個 worker 都有完整 copy、寫入 coordinator 廣播給所有 worker。

用途:

  • 小 lookup table(country code / product category 等)
  • 跨 distributed table JOIN 時、reference table 在每 worker 上、不必 cross-shard
  • 寫入頻率低(廣播 cost 跟 worker 數 linear)

Local table — Coordinator 上的 PG table

1CREATE TABLE audit_log (
2    id SERIAL PRIMARY KEY,
3    event JSONB
4);
5-- 不調用 Citus function、預設留在 coordinator

行為跟一般 PG table 一樣。用於 不需 distribute 的 table(如 admin metadata)。

Colocation:跨 distributed table 同 shard 對齊

當兩個 distributed table 都用 同 distribution column(例如 user_id)+ 同 shard count、Citus 自動 colocate:

1SELECT create_distributed_table('orders', 'user_id');
2SELECT create_distributed_table('user_addresses', 'user_id', colocate_with => 'orders');

Colocate 後:

  • user_id = 100 的 orders 跟 user_addresses 在 同一 worker shard
  • JOIN 不跨 worker、效率高
  • 可用 PG 原生 FK constraint(cross-table 但同 shard)

Colocate 是 Citus 設計的核心 跨 table 一致性 機制。沒 colocate 的 cross-table query 變 cross-worker、效率大降。

配置 step-by-step(local cluster)

Production 用 Citus Cloud(Microsoft 託管)或 Azure Cosmos DB for PostgreSQL(同 engine)。Self-hosted:

Step 1:Coordinator + worker 都裝 PG + Citus

1# 在每個 node(coordinator + 2 worker)
2apt install postgresql-14
3apt install postgresql-14-citus-12.0
4
5# postgresql.conf
6shared_preload_libraries = 'citus'
7
8systemctl restart postgresql
1-- 在每個 node 跑
2CREATE EXTENSION citus;

Step 2:Coordinator 註冊 worker

1-- 在 coordinator 跑
2SELECT citus_add_node('worker1.example.com', 5432);
3SELECT citus_add_node('worker2.example.com', 5432);
4
5-- 確認
6SELECT * FROM citus_get_active_worker_nodes();

Step 3:建 distributed table

1CREATE TABLE orders (
2    id BIGSERIAL,
3    user_id BIGINT NOT NULL,
4    amount DECIMAL(10,2),
5    created_at TIMESTAMP,
6    PRIMARY KEY (user_id, id)
7);
8
9SELECT create_distributed_table('orders', 'user_id');

Citus 自動把 orders 拆成 32 個 shard(orders_102008 等)、分配到 worker。

Step 4:Application 連 coordinator

Application connection string 連 coordinator IP / port(不必知道 worker 存在)。

1-- 從 application 跑 query、Citus 透明 route
2INSERT INTO orders (user_id, amount) VALUES (12345, 50);
3-- → Citus 看 user_id=12345 hash 屬 shard 17、route 給對應 worker
4
5SELECT * FROM orders WHERE user_id = 12345;
6-- → Single-shard query、極快
7
8SELECT count(*) FROM orders;
9-- → Cross-shard aggregation、Citus 並行跑、合併結果

5 個 Production 踩雷

1. Distribution column 選錯 — Cross-shard query 變主流

created_atid(auto increment)作 distribution column、看起來均勻、實際 application query 多以 user_id 為主、變成 每個 query 都 cross-shard、performance 雪崩。

修法:

  • Distribution column 選 application 最常 filter / join 的 column(通常是 tenant_id / user_id
  • Audit application top query、確認 distribution column 對齊 query pattern
  • 改 distribution column 要 rewrite 所有 shard、像 resharding、大工程

2. Cross-shard transaction 限制

跨多 shard 的 transaction(如:UPDATE 兩個 user_id 不同的 row)Citus 用 2PC(two-phase commit)但有限制:

  • Multi-statement transaction 跨 shard 需明確開 SET citus.multi_shard_modify_mode = 'sequential'
  • 部分 isolation level 不保證 serializable across shards
  • DDL 跨 shard 是 sequential

修法:

  • Schema design 避免 cross-shard transaction(同 colocation group 內 transaction 沒問題)
  • 必要 cross-shard 場景明確設 multi-shard mode
  • strict cross-shard consistency、考慮 distributed SQL(CockroachDB / Aurora DSQL)

3. Reference table 過大 — 寫入廣播 cost 爆

Reference table 在每 worker 都有 copy、寫入 廣播給所有 worker。Reference table 100K row + 高頻寫入 → 寫一次寫 N worker、cost N x。

修法:

  • Reference table 限 小 + 寫入頻率低 的 lookup data
  • 超大表不該是 reference table、考慮 distributed
  • 監控 reference table 寫入 rate、超 threshold 重新評估

4. Colocate 沒對齊 — 隱性 cross-shard JOIN

1-- 看似可以、實際 cross-shard 慢
2SELECT * FROM orders o JOIN user_addresses ua ON o.user_id = ua.user_id;

user_addressescolocate_with => 'orders'、兩表 shard 分配獨立、JOIN 跨 worker。

修法:

  • 建相關 table 時 colocate_with 對齊
  • SELECT * FROM citus_tables 看 colocation_id、確認對齊
  • 跨非 colocate table 的 JOIN 用 materialized view 或 application 層拆 query 避開

5. Worker failover — Coordinator 必須知道

Worker 故障、Citus 預設 coordinator 看到 query 失敗、不自動 failover

修法(Citus 11+):

  • shard replicationcitus.shard_replication_factor = 2)— 每 shard 在 2 個 worker 有 copy
  • 配 PG streaming replication 在 worker 層、外加 Patroni 管 failover
  • Coordinator 失敗 → 整個 cluster 失能、coordinator 也要 HA(Patroni)

跟 Vitess 對比 Citus 的 HA story 較弱、production 必須認真規劃。

何時用 Citus

條件建議
Multi-tenant SaaS、tenant_id 為自然 distribution
寫吞吐 > 50K WPS、單 PG 撐不住
需要保留 PG SQL + extension(pgvector / TimescaleDB)
應用 query pattern 80% 都用同一 distribution column
應用大量 ad-hoc cross-tenant aggregation否(cross-shard 慢)
強 cross-shard consistency 需求否(用 CockroachDB)
想 zero-ops managedAzure Cosmos DB for PostgreSQL(同 engine)

容量規劃

  • Coordinator: 中等 CPU + RAM、metadata 不大、不存 data
  • Worker: per-worker spec 同 single PG production
  • Shard count: 預設 32、實務常設 worker count × 4-8
  • Replication factor: production 至少 2

跟其他模組整合

跟 Replication topology

Coordinator + worker 各跑 PG streaming replication、Citus 不取代 PG replication。Worker failover 用 Patroni / streaming replication。詳見 Replication Topology

跟 PG Extensions

Citus 跟其他 PG extension 多數兼容(pgvector / TimescaleDB / pg_stat_statements)— 它維持 extension 形態,保留 PostgreSQL 生態接點。詳見 PG Extension Ecosystem 篇(待寫)。

跟 MySQL Vitess

維度CitusVitess
部署模型PG extension獨立 proxy + tablet
主要場景Multi-tenant SaaS超大規模分片
Cross-shard JOINcolocate 對齊 + reference tableVTGate 自動 split + aggregate
FK同 colocation 內可用Vitess 18+ 支援、cross-shard 限制
HA依賴 Patroni + replication factorVTOrc + replication
學習曲線中(PG ops 經驗夠)高(4 component)

Citus 對 PG-native 場景更平順、Vitess 對 MySQL-native 場景更平順、不直接競爭。詳見 MySQL Vitess Sharding

相關連結