Citus 分散式 PostgreSQL:水平擴展到多節點叢集 | PostgreSQL
當單機 PostgreSQL 的容量與吞吐量已無法滿足業務成長,Citus 分散式擴展提供了一條不換資料庫、不離開 PG 生態的水平擴展之路。本文將從 Coordinator-Workers 架構到 Colocation 共置機制,從 Hash 分片策略到 Schema-based Sharding,完整解析如何將 PostgreSQL 擴展為多節點分散式叢集。
系列最終篇:為什麼需要分散式?
在前 49 篇文章中,我們從 PostgreSQL 基礎語法一路走到雲端託管與開發者工具。但有一個問題始終懸而未決——當資料量超過單機極限,該怎麼辦?
垂直擴展(Scale-Up)終有天花板:記憶體、CPU、磁碟 I/O 都受限於單一主機的物理規格。當你面對以下場景時,水平擴展(Scale-Out)成為必要選擇:
- 資料量超過 2TB,單機存儲與備份壓力過大
- 寫入吞吐量突破 單一 Primary 的瓶頸(讀取副本無法分攤寫入)
- 多租戶 SaaS 需要將租戶資料分散在不同節點以實現隔離
- 時序/事件資料持續增長,需要無限水平擴容
Citus 正是解決這類問題的 PostgreSQL 原生方案——它以 Extension 形式存在,不需要換資料庫引擎,所有 PostgreSQL 的 SQL 語法、函數、Extension 都能繼續使用。
Citus 是什麼?
Citus 是 PostgreSQL 的分散式擴展(Distributed Extension),透過水平分片(Sharding)機制將單一資料庫擴展為多節點叢集。最初由 Citus Data 公司開發,2019 年被微軟收購,現已完全開源。
核心定位:
| 項目 | 說明 |
|---|---|
| 類型 | PostgreSQL Extension(CREATE EXTENSION citus) |
| 授權 | AGPL-3.0(完全開源) |
| 雲端服務 | Azure Database for PostgreSQL – Flexible Server |
| 最新版本 | Citus 13.x(2025,支援 PG 17) |
| 適用場景 | 多租戶 SaaS、大規模分析、時序資料 |
Coordinator-Workers 架構
Citus 採用**協調者-工作者(Coordinator-Workers)**架構,所有節點都是完整的 PostgreSQL 實例:
┌─────────────────────────────────────────────────────┐
│ 應用程式 / Client │
│ (連接 Coordinator,行為與普通 PG 相同) │
└────────────────────────┬────────────────────────────┘
│ 標準 PostgreSQL 連線
▼
┌─────────────────────────────────────────────────────┐
│ Coordinator 節點 │
│ ┌───────────────────────────────────────────────┐ │
│ │ Query Planner(分散式查詢計畫器) │ │
│ │ ├─ 解析 SQL → 判斷是否涉及分散表 │ │
│ │ ├─ 產生分散式查詢計畫 │ │
│ │ ├─ 路由至對應 Shard 所在 Worker │ │
│ │ └─ 合併各 Worker 的結果並回傳 │ │
│ ├───────────────────────────────────────────────┤ │
│ │ Metadata(元資料) │ │
│ │ ├─ pg_dist_node:節點清單 │ │
│ │ ├─ pg_dist_shard:Shard 與分片範圍 │ │
│ │ └─ pg_dist_placement:Shard 在哪個 Worker │ │
│ └───────────────────────────────────────────────┘ │
└──────────┬──────────────┬──────────────┬────────────┘
│ │ │
┌──────▼─────┐ ┌─────▼──────┐ ┌───▼──────────┐
│ Worker 1 │ │ Worker 2 │ │ Worker N │
│ ┌────────┐ │ │ ┌────────┐ │ │ ┌──────────┐ │
│ │Shard 1 │ │ │ │Shard 2 │ │ │ │Shard N-1 │ │
│ │Shard 5 │ │ │ │Shard 6 │ │ │ │Shard N │ │
│ └────────┘ │ │ └────────┘ │ │ └──────────┘ │
└────────────┘ └────────────┘ └──────────────┘
角色分工
Coordinator(協調者):
- 接收所有應用程式的 SQL 連線
- 解析查詢,判斷哪些 Shard 需要被訪問
- 將查詢拆解並**平行下推(Push Down)**至對應 Worker
- 合併各 Worker 回傳結果
- 維護分散式元資料
Worker(工作者):
- 儲存實際的 Shard 資料(每個 Shard 是一個普通的 PostgreSQL 表)
- 執行 Coordinator 下推的子查詢
- 可直接接受其他 Worker 的連線(Shard 間資料移動時)
重要限制
- 應用程式只連接 Coordinator,不直接連接 Worker
- Coordinator 本身不儲存分散表資料,只儲存元資料與參考表
- 單點故障風險:Coordinator 不可用時整個叢集無法接受新查詢(需配合 HA 方案)
分散表(Distributed Table)
分散表是 Citus 的核心概念。普通表轉換為分散表後,資料依照**分片鍵(Distribution Column)**分散儲存在各 Worker 的 Shard 中:
-- 建立普通表
CREATE TABLE orders (
order_id BIGINT NOT NULL,
customer_id BIGINT NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
amount NUMERIC(12,2),
status TEXT
);
-- 轉換為分散表,以 customer_id 為分片鍵
SELECT create_distributed_table('orders', 'customer_id');
-- Citus 自動建立 32 個 Shard(預設值),分布在所有 Worker 上
-- orders_102008, orders_102009, ... 每個都是 Worker 上的真實 PG 表
分片鍵的選擇原則
分片鍵的選擇是 Citus 架構設計中最重要的決策:
| 原則 | 說明 | 範例 |
|---|---|---|
| 高基數(High Cardinality) | 避免資料傾斜 | customer_id 優於 status |
| 查詢對齊 | 大多數查詢應包含分片鍵 | WHERE 條件常帶 customer_id |
| JOIN 對齊 | 需 JOIN 的表用相同分片鍵 | orders 與 order_items 共用 tenant_id |
| 不可變性 | 分片鍵值不可 UPDATE | Citus 不支援跨 Shard 的分片鍵更新 |
參考表(Reference Table)
參考表是完整複製到每個 Worker的小型資料集,適合維度資料或頻繁 JOIN 的查詢表:
-- 建立參考表
CREATE TABLE countries (
country_code CHAR(2) PRIMARY KEY,
country_name TEXT NOT NULL,
region TEXT
);
SELECT create_reference_table('countries');
-- 分散表與參考表的 JOIN 在各 Worker 本地完成(零跨節點通訊)
SELECT o.order_id, o.amount, c.country_name
FROM orders o
JOIN countries c ON o.country_code = c.country_code
WHERE o.customer_id = 12345;
-- 路由到單一 Worker,JOIN 在本地完成
分散表 vs 參考表
| 特性 | 分散表 | 參考表 |
|---|---|---|
| 資料規模 | 百萬至億筆 | 數千至數萬筆 |
| 儲存方式 | 分片,各 Worker 部分資料 | 全量複製,各 Worker 完整資料 |
| JOIN 效率 | 需分片鍵對齊 | 可與任何表在本地 JOIN |
| 寫入開銷 | 僅寫入對應 Shard | 需同步寫入所有 Worker(2PC) |
| 適用場景 | 主要業務資料表 | 國家/幣別/類別等查詢表 |
Shard 策略:Hash vs Append
Hash Distribution(預設,最常用)
SELECT create_distributed_table('orders', 'customer_id');
運作原理:
customer_id = 1001
│
▼
hash(1001) = 394857293
│
▼
394857293 mod 32 = 13 (Shard index)
│
▼
Shard 13 → Worker 2
特點:
- 資料均勻分散,避免熱點
- 適合 OLTP 工作負載
- 支援
=和IN條件的 Shard Pruning - 範圍查詢(
BETWEEN、>)需掃描所有 Shard
Append Distribution(追加分片)
SELECT create_distributed_table('events', 'created_at', 'append');
特點:
- 每個 Shard 儲存特定範圍(如時間區間)
- 適合 Append-Only 工作負載(日誌、事件流)
- 現代架構中較少使用,多以 Hash 分片 + 時間分割取代
策略比較
| 特性 | Hash | Append |
|---|---|---|
| 資料分布 | 均勻雜湊分散 | 依範圍追加 |
| 適用場景 | OLTP、多租戶 | 時序、批次 ETL |
| Shard 管理 | 自動 | 手動 |
| UPDATE/DELETE | 支援 | 不建議 |
| 範圍查詢 | 需掃描所有 Shard | 可利用範圍裁剪 |
分散式查詢路由
Citus 的查詢路由分為兩種模式,效能差異巨大:
Router Query(路由查詢)— 最佳路徑
當查詢包含分片鍵的等值過濾時,Coordinator 精確定位到單一 Shard:
-- Router Query:定位到單一 Shard,效能等同單機 PG
SELECT * FROM orders WHERE customer_id = 12345;
INSERT INTO orders (order_id, customer_id, amount)
VALUES (9001, 12345, 99.99);
UPDATE orders SET status = 'shipped' WHERE customer_id = 12345;
執行計畫:
Custom Scan (Citus Router)
Task Count: 1
-> Task
Node: host=worker1 port=5432
-> Index Scan using orders_pkey on orders_102009
Filter: (customer_id = 12345)
Real-Time Query(實時查詢)— 平行掃描
不包含分片鍵或需要跨 Shard 聚合時,平行下推到所有 Worker:
-- Real-Time Query:平行掃描所有 Shard
SELECT status, COUNT(*), SUM(amount)
FROM orders
GROUP BY status;
-- 沒有分片鍵過濾,需掃描所有 Shard
SELECT * FROM orders WHERE amount > 1000;
執行計畫:
Custom Scan (Citus Real-Time)
Task Count: 32
Tasks Shown: One of 32
-> Task
Node: host=worker1 port=5432
-> HashAggregate
-> Seq Scan on orders_102008
跨 Shard JOIN 的挑戰
-- 問題:orders 和 shipments 分片鍵不同,需大量跨節點資料移動
SELECT o.order_id, s.tracking_number
FROM orders o -- 以 customer_id 分片
JOIN shipments s ON o.order_id = s.order_id; -- 以 order_id 分片
-- 解決方案:加上分片鍵條件 + Subquery Pushdown
SELECT o.order_id, s.tracking_number
FROM orders o
JOIN (SELECT order_id, tracking_number FROM shipments) s
ON o.order_id = s.order_id
WHERE o.customer_id = 12345; -- 加上分片鍵
Colocation(資料共置)
Colocation 是 Citus 效能最佳化的核心機制。當多張分散表使用相同分片鍵與分片數量時,Citus 保證相同鍵值的資料落在同一個 Worker:
-- 以 tenant_id 為分片鍵,所有表共置
SELECT create_distributed_table('orders', 'tenant_id');
SELECT create_distributed_table('customers', 'tenant_id', colocate_with => 'orders');
SELECT create_distributed_table('products', 'tenant_id', colocate_with => 'orders');
SELECT create_distributed_table('invoices', 'tenant_id', colocate_with => 'orders');
-- 以下 JOIN 完全在本地執行,零跨節點通訊
SELECT c.name, o.order_id, o.amount, i.invoice_number
FROM customers c
JOIN orders o ON c.tenant_id = o.tenant_id AND c.customer_id = o.customer_id
JOIN invoices i ON i.tenant_id = o.tenant_id AND i.order_id = o.order_id
WHERE c.tenant_id = 'tenant_abc'
AND c.customer_id = 12345;
-- 路由到單一 Worker,零跨節點通訊
Colocation 的資料分布視覺化:
Tenant A (tenant_id = 'abc') → Shard Group 5 → Worker 2
├─ orders_102015 (Tenant A 的訂單)
├─ customers_102015 (Tenant A 的客戶)
├─ products_102015 (Tenant A 的產品)
└─ invoices_102015 (Tenant A 的發票)
Tenant B (tenant_id = 'xyz') → Shard Group 12 → Worker 1
├─ orders_102022
├─ customers_102022
├─ products_102022
└─ invoices_102022
查看 Colocation Group:
-- 確認各表的 Colocation Group
SELECT logicalrelid AS table_name, colocationid
FROM pg_dist_partition
ORDER BY colocationid, logicalrelid;
Schema-based Sharding(Citus 12+)
Citus 12 引入了基於 Schema 的分片機制,這是多租戶 SaaS 的重大改進。每個租戶擁有獨立的 PostgreSQL Schema,Citus 自動將整個 Schema 分配到對應 Worker:
-- 啟用 Schema-based Sharding
SET citus.enable_schema_based_sharding TO on;
-- 為每個租戶建立獨立 Schema
CREATE SCHEMA tenant_abc;
CREATE SCHEMA tenant_xyz;
-- 在 Schema 內建表(不需要呼叫 create_distributed_table!)
CREATE TABLE tenant_abc.orders (
order_id BIGINT PRIMARY KEY,
amount NUMERIC(12,2),
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE tenant_abc.customers (
customer_id BIGINT PRIMARY KEY,
name TEXT NOT NULL,
email TEXT
);
-- Citus 自動將整個 tenant_abc schema 分配到某個 Worker
-- tenant_abc 內的所有表自動 Collocated!
Column-based vs Schema-based
| 特性 | Column-based(傳統) | Schema-based(Citus 12+) |
|---|---|---|
| 分片單元 | 行(依分片鍵欄位值) | Schema(整個 Schema 到一個 Worker) |
| 設定複雜度 | 每張表需呼叫 create_distributed_table | 只需建立 Schema |
| 跨表隔離 | 同一表混合多個租戶 | 每個租戶有獨立 Schema |
| 資料隔離 | 邏輯隔離 | Schema 級隔離 |
| 適用規模 | 租戶數百萬,每租戶資料少 | 租戶數千,每租戶資料多 |
| RLS 需求 | 需要額外 RLS 設定 | Schema 天然隔離 |
| 搬移粒度 | Shard(行子集) | 整個 Schema |
安裝與叢集設定
# Ubuntu/Debian 安裝 Citus(以 PG 16 為例)
curl https://install.citusdata.com/community/deb.sh | sudo bash
sudo apt-get install -y postgresql-16-citus-12.1
# 設定 shared_preload_libraries(必須,需重啟)
echo "shared_preload_libraries = 'citus'" >> /etc/postgresql/16/main/postgresql.conf
sudo systemctl restart postgresql
-- 在每個節點(Coordinator 和所有 Worker)執行
CREATE EXTENSION citus;
-- 在 Coordinator 上新增 Worker 節點
SELECT citus_add_node('worker1-hostname', 5432);
SELECT citus_add_node('worker2-hostname', 5432);
SELECT citus_add_node('worker3-hostname', 5432);
-- 驗證節點狀態
SELECT * FROM citus_get_active_worker_nodes();
-- 查看完整節點清單
SELECT nodeid, nodename, nodeport, isactive, noderole
FROM pg_dist_node
ORDER BY nodeid;
分散式 DDL 與限制
Citus 中的 DDL 會自動廣播到所有 Worker:
-- 自動應用到所有 Shard
ALTER TABLE orders ADD COLUMN notes TEXT;
CREATE INDEX CONCURRENTLY idx_orders_status ON orders(status);
-- 外鍵約束必須包含分片鍵
ALTER TABLE orders ADD CONSTRAINT fk_customer
FOREIGN KEY (tenant_id, customer_id)
REFERENCES customers(tenant_id, customer_id);
-- 注意:orders 和 customers 必須 Colocated
-- 不允許的操作
UPDATE orders SET customer_id = 99999 WHERE order_id = 1;
-- ERROR: modifying the partition column of a row is not allowed
Shard 管理:
-- 查看分散表設定
SELECT logicalrelid AS table_name, partmethod, partkey, colocationid
FROM pg_dist_partition
ORDER BY logicalrelid;
-- 查看 Shard 分布
SELECT s.logicalrelid, s.shardid, sp.nodename, sp.nodeport
FROM pg_dist_shard s
JOIN pg_dist_shard_placement sp ON s.shardid = sp.shardid
WHERE s.logicalrelid = 'orders'::regclass
ORDER BY s.shardid;
-- 調整 Shard 數量(建表前設定)
SET citus.shard_count = 64; -- 預設 32
SELECT create_distributed_table('large_table', 'user_id');
-- 查詢特定記錄在哪個 Worker
SELECT get_shard_id_for_distribution_column('orders', 12345);
Shard 重新平衡
新增 Worker 後,需要重新平衡 Shard 分布:
-- 自動重新平衡
SELECT rebalance_table_shards('orders');
-- 手動搬移特定 Shard
SELECT citus_move_shard_placement(
shardid,
'source_worker', 5432,
'target_worker', 5432
)
FROM pg_dist_shard_placement
WHERE nodename = 'source_worker'
LIMIT 5;
-- 查看重新平衡進度
SELECT * FROM get_rebalance_progress();
多租戶 SaaS 實戰
Citus 最成熟的使用場景是多租戶 SaaS,以 tenant_id 為統一分片鍵:
-- === Step 1: 建立分散表並共置 ===
SELECT create_distributed_table('tenants', 'tenant_id');
SELECT create_distributed_table('users', 'tenant_id', colocate_with => 'tenants');
SELECT create_distributed_table('orders', 'tenant_id', colocate_with => 'tenants');
SELECT create_distributed_table('products', 'tenant_id', colocate_with => 'tenants');
-- === Step 2: 建立參考表(跨租戶共用資料) ===
SELECT create_reference_table('payment_methods');
SELECT create_reference_table('shipping_zones');
-- === Step 3: 設定 Row Level Security ===
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON orders
USING (tenant_id = current_setting('app.current_tenant')::bigint);
-- === Step 4: 應用程式設定租戶 context ===
SET LOCAL app.current_tenant = '12345';
SELECT * FROM orders WHERE status = 'pending';
-- 自動過濾:只返回 tenant_id = 12345 的訂單
-- === Step 5: 管理員跨租戶查詢 ===
RESET app.current_tenant;
SELECT tenant_id, COUNT(*) AS order_count, SUM(amount) AS total
FROM orders
GROUP BY tenant_id
ORDER BY total DESC;
架構優勢:
┌─ 應用程式 ─┐ ┌─ Coordinator ─┐ ┌─ Workers ──────────┐
│ │ │ │ │ │
│ SET tenant │─────►│ 解析 SQL │─────►│ Worker 1: Tenant A │
│ → 查詢 │ │ + 路由到 │ │ Worker 2: Tenant B │
│ │ │ 對應 Worker │ │ Worker 3: Tenant C │
└────────────┘ └───────────────┘ └────────────────────┘
每個租戶的所有表資料(orders, customers, products)
都在同一個 Worker 上 → 零跨節點通訊
時序資料架構(Citus + 分割表)
Citus 可與 PostgreSQL 原生分割表結合,實現分散式時序架構——同時利用 Partition Pruning 和 Shard Pruning 的雙重裁剪:
-- 建立時序分散表(先分散後分割)
CREATE TABLE metrics (
device_id BIGINT NOT NULL,
recorded_at TIMESTAMPTZ NOT NULL,
metric_name TEXT NOT NULL,
value DOUBLE PRECISION,
tags JSONB
) PARTITION BY RANGE (recorded_at);
-- 將父表設為分散表
SELECT create_distributed_table('metrics', 'device_id');
-- 建立時間分割子表
CREATE TABLE metrics_2026_05 PARTITION OF metrics
FOR VALUES FROM ('2026-05-01') TO ('2026-06-01');
CREATE TABLE metrics_2026_06 PARTITION OF metrics
FOR VALUES FROM ('2026-06-01') TO ('2026-07-01');
-- 查詢效率極高:Partition Pruning + Shard Pruning 雙重裁剪
SELECT AVG(value)
FROM metrics
WHERE device_id = 1001 -- Shard Pruning
AND recorded_at BETWEEN '2026-05-01' AND '2026-05-31'; -- Partition Pruning
-- 只訪問 metrics_2026_05 分割,且只在 device_id=1001 所在的 Worker 上執行
分散式交易
-- Citus 支援分散式交易(透過兩階段提交 2PC)
BEGIN;
INSERT INTO orders (tenant_id, order_id, amount)
VALUES (100, 9001, 150.00);
INSERT INTO order_items (tenant_id, order_id, product_id, qty)
VALUES (100, 9001, 5, 2);
COMMIT;
-- 若 Colocated,此交易在單一 Worker 本地執行(效能最佳)
-- 涉及多個 Worker 時自動使用 2PC
BEGIN;
UPDATE orders SET status = 'cancelled'
WHERE order_id IN (1, 2, 3, 4, 5);
-- 若這些 order_id 分布在不同 Worker,Citus 自動 2PC
COMMIT;
不支援的操作:
- 跨分片鍵的 UPDATE(無法在分片間移動行)
- 修改分散表的分片鍵欄位值
- 涉及多個非共置分散表的
FOR UPDATE鎖
效能調優
-- 1. 啟用 Repartition Joins
SET citus.enable_repartition_joins = on;
-- 2. 調整平行查詢
SET citus.max_adaptive_executor_pool_size = 16;
-- 3. 分析查詢計畫,識別跨節點資料移動
EXPLAIN (ANALYZE, VERBOSE)
SELECT customer_id, SUM(amount)
FROM orders
GROUP BY customer_id
HAVING SUM(amount) > 10000;
-- 觀察是否有 Repartition 或 Broadcast 步驟
-- 4. Worker 內部平行度(各 Worker 的 postgresql.conf)
-- max_parallel_workers_per_gather = 4
-- 5. 複合索引策略(分片鍵必須在索引中)
CREATE INDEX idx_orders_customer_created
ON orders (customer_id, created_at DESC);
-- 6. Coordinator 前端建議使用 PgBouncer 做連線池
監控與除錯
-- 查看分散式查詢的執行計畫
EXPLAIN SELECT COUNT(*) FROM orders WHERE customer_id = 12345;
-- 查看 Coordinator 活躍連線
SELECT * FROM citus_dist_stat_activity;
-- 查看 Worker 任務執行
SELECT * FROM citus_worker_stat_activity;
-- 查看各 Shard 資料大小
SELECT table_name, shard_id, shard_size
FROM citus_shards
WHERE table_name = 'orders'::regclass
ORDER BY shard_size DESC;
-- 慢查詢分析
SELECT * FROM citus_stat_statements
ORDER BY total_time DESC LIMIT 20;
-- 節點健康狀態
SELECT nodename, nodeport, isactive, shouldhaveshards
FROM pg_dist_node;
高可用架構
Citus 本身不提供節點層面的高可用,需整合 PostgreSQL 複寫與故障轉移工具:
┌──────────────────────────────────────────────────────────┐
│ Citus HA 架構 │
├──────────────────────────────────────────────────────────┤
│ │
│ Coordinator Primary ──Streaming Replication──► Standby │
│ │ │
│ (Patroni / pg_auto_failover 管理 Coordinator HA) │
│ │
│ Worker 1 Primary ──► Worker 1 Standby │
│ Worker 2 Primary ──► Worker 2 Standby (各自獨立 HA) │
│ Worker N Primary ──► Worker N Standby │
│ │
└──────────────────────────────────────────────────────────┘
-- 查看各節點健康
SELECT nodename, nodeport, isactive FROM pg_dist_node;
-- Worker 故障後標記為不活躍
SELECT citus_disable_node('failed_worker', 5432);
-- 恢復後重新啟用
SELECT citus_activate_node('recovered_worker', 5432);
Citus vs 其他方案
Citus vs Cloud SQL / AlloyDB
| 維度 | Citus | Cloud SQL / AlloyDB |
|---|---|---|
| 擴展模式 | 水平分片(Scale-Out) | 垂直擴展 + 讀取副本 |
| PG 相容性 | 高度相容,有分散式限制 | 完全相容 |
| 管理複雜度 | 高(需設計分片策略) | 低(全託管) |
| 最大資料量 | 近乎無限(加 Worker) | 受單機規格限制 |
| 寫入擴展 | 是(多 Writer) | 否(單 Primary) |
| 跨節點 JOIN | 受限(需 Colocation) | 完全支援 |
| 適用場景 | 超大規模多租戶、分析 | 一般 OLTP |
Citus vs Google Spanner
| 維度 | Citus | Spanner |
|---|---|---|
| 全球分散 | 需額外設計 | 原生全球分散 |
| 強一致性 | 本地強一致 | 全球強一致(TrueTime) |
| SQL 相容性 | PostgreSQL SQL | Spanner SQL |
| 自動 Sharding | 半自動(需設計分片鍵) | 完全自動 |
| 費用 | 較低(可自建) | 極高 |
| 生態系 | PostgreSQL 完整生態 | 獨立生態 |
選擇決策流程
需要分散式資料庫?
├─ 否 → 單機 PostgreSQL(+ HA 複寫)
└─ 是
├─ 需要全球強一致性?
│ ├─ 是 → Google Spanner
│ └─ 否
│ ├─ 有明確的 tenant_id 或分片鍵?
│ │ ├─ 是 → Citus(效能最佳)
│ │ └─ 否 → AlloyDB 或重新設計 Schema
│ ├─ 可接受 PG 相容性限制?
│ │ ├─ 是 → Citus
│ │ └─ 否 → AlloyDB 或升級單機規格
│ └─ 預算有限且可自建?
│ ├─ 是 → 自建 Citus
│ └─ 否 → Azure Cosmos DB for PostgreSQL
版本演進
| 版本 | 年份 | 重要功能 |
|---|---|---|
| Citus 5.x | 2015 | 初始開源版本,基礎 Hash 分片 |
| Citus 7.x | 2018 | 分散式 DDL、Append Distribution 改進 |
| Citus 9.x | 2019 | 微軟收購,效能大幅提升 |
| Citus 10.x | 2021 | 完全開源(含原企業版功能),Columnar Storage |
| Citus 11.x | 2022 | 支援所有節點直連 |
| Citus 12.x | 2023 | Schema-based Sharding |
| Citus 13.x | 2025 | PG 17 支援、分散式查詢效能提升 |
總結
Citus 作為 PostgreSQL 的分散式擴展,提供了一條不離開 PG 生態的水平擴展之路。掌握以下核心觀念,就能駕馭 Citus 的分散式威力:
- 分片鍵決定一切——選對分片鍵(通常是
tenant_id),Router Query 的效能等同單機 PG - Colocation 是效能關鍵——共置的表可以在本地 JOIN,避免跨節點資料移動
- 參考表解決維度 JOIN——小型查詢表複製到每個 Worker,與任何表本地 JOIN
- Schema-based Sharding(Citus 12+)——多租戶 SaaS 的更直覺選擇,Schema 級隔離
- 不是所有場景都需要分散式——資料量 < 2TB 時,單機 PG + HA 通常是更好的選擇
至此,PostgreSQL 完全指南 50 篇系列全部完成。從第 1 篇的安裝與基礎 SQL,到這一篇的分散式擴展,我們走過了 PostgreSQL 從入門到精通的完整旅程。希望這個系列能成為你在 PostgreSQL 世界中持續前行的堅實基礎。