Cosmos DB for PostgreSQL:基於 Citus 的分散式 PostgreSQL、跟核心 Cosmos DB 是不同產品、何時選它而非核心 Cosmos 或一般 PG
這篇整理 Cosmos DB for PostgreSQL 的定位、Citus 架構與選型標準:它跟核心 Cosmos DB 的差別、何時選它、何時該回核心 Cosmos DB、何時一般 PostgreSQL 就夠。Cosmos DB for PostgreSQL 是 Azure 在 2022 把 Citus(PostgreSQL 的分散式 extension)納入後推出的 分散式 PostgreSQL 託管服務 — 它跑真正的 PostgreSQL engine、支援標準 SQL / JOIN / ACID 交易、把單表水平分片到多個 worker node。它跟核心 Cosmos DB(NoSQL、multi-model、RU/s 計費)是 兩個不同產品、只是共用品牌名稱。
本文沒有專屬 production case anchor:Cosmos DB for PostgreSQL 的公開 case 覆蓋稀薄、機制以 Azure / Citus vendor 規格與分散式 PostgreSQL 通用工程展開、選型標準用「scale-out PG vs NoSQL vs single-node PG」這個具體決策驅動。
Scope warning:本文涉及的服務命名、node 規格上限、Citus 版本、PostgreSQL major version 支援屬時間敏感、Azure 服務命名歷史上有變動、實作前以 Cosmos DB for PostgreSQL 官方文件 cross-verify。
問題情境
典型觸發場景:team 在 Azure 上跑 PostgreSQL、單機 primary 撐到上限 — write throughput、資料量、或單表太大導致 index / vacuum / query 變慢。看到「Cosmos DB」以為是要把資料搬進 NoSQL、重寫 application 成 document model;或反過來、看到「Cosmos DB for PostgreSQL」以為它就是核心 Cosmos DB 的一個 PostgreSQL API、結果發現它是完全不同的東西。命名混淆讓選型從一開始就走偏。
讀者徵兆:
- 「單機 PostgreSQL 撐不住、但 application 是 SQL / JOIN / 交易重、不想重寫成 NoSQL」
- 「Cosmos DB for PostgreSQL 跟核心 Cosmos DB 是同一個東西嗎」
- 「它跟一般 Azure Database for PostgreSQL 差在哪、什麼時候才需要它」
- 「跟 CockroachDB / Aurora / Spanner 這些 distributed SQL 怎麼選」
真實壓力:SQL workload 撐到單機上限時、選錯方向的成本是年級的。誤以為要遷 NoSQL 而重寫 application 是浪費;誤以為核心 Cosmos DB 有「PostgreSQL 相容」而選錯產品也是浪費。正確的選型要先把這個服務放回它真正的分類 — 分散式 SQL、見 distributed SQL。
核心機制:Citus-based coordinator-worker 分散式 PostgreSQL
Cosmos DB for PostgreSQL 的底層是 Citus、把 PostgreSQL 從單機擴展成 coordinator + worker 的分散式叢集。理解它要看 engine 是什麼、coordinator 與 worker 怎麼分工、distribution column 決定什麼、表有哪幾種。
它跑 真正的 PostgreSQL。不是 wire-compat、不是 PostgreSQL API on top of NoSQL — 是 PostgreSQL engine 加 Citus extension。標準 SQL、JOIN、ACID 交易、PostgreSQL extension 生態(含部分如 PostGIS)都在。這跟核心 Cosmos DB(自己的 query language、SQL-like 而 JOIN 只能在單一 document 內部做、RU/s 計費)是根本不同的產品。
架構是 coordinator-worker。coordinator node 接 query、根據 distribution column 把 query 路由 / 拆分到 worker node、worker 存實際的 shard。application 連 coordinator、看起來像連一個 PostgreSQL。
distribution column 是核心設計決策、類比核心 Cosmos DB 的 partition key 之於 NoSQL、也類比 partition-key-design 講的分散原則。表按 distribution column 的值分片到 worker;同一 distribution column 值的 row 落在同一 shard。JOIN 與交易若在同一 distribution column 值內、可以下推到單一 worker 高效執行(co-location);跨 distribution column 的 JOIN / 交易要跨 worker 協調、較貴。
表分三種:distributed table(按 distribution column 分片、大表用)、reference table(每個 worker 全複本、小的維度表用、讓 JOIN co-locate)、local table(只在 coordinator)。建模的關鍵是把常一起 JOIN 的大表用 同一 distribution column 分片、達成 co-location。
選型標準:Cosmos DB for PostgreSQL、核心 Cosmos DB 與單機 PostgreSQL 的分界
Cosmos DB for PostgreSQL 的正確位置是「single-node PG 不夠、但 workload 仍是 SQL 範式」的中間地帶。
選 Cosmos DB for PostgreSQL 的條件:
- workload 是 SQL 範式(關聯 schema、JOIN、交易)、不想 / 不能重寫成 NoSQL
- single-node PostgreSQL 已達上限(write throughput / 資料量 / 單表大小)、且資料有好的 distribution column(多租戶的 tenant_id、time-series 的某維度)
- 工作負載偏向多租戶 SaaS 或 real-time analytics over fresh data — Citus 的典型適配場景
- 想留在 PostgreSQL 生態(SQL、extension、既有 tooling)而非進 NoSQL
回核心 Cosmos DB(NoSQL)的條件:
- 資料形狀已是 document / KV、access pattern 固定、不需要 JOIN 與複雜 SQL
- 需要 multi-model(document + graph + KV)、5 個 consistency level、turnkey multi-region active-active write
- RU/s 容量抽象與 serverless 計費更符合 workload — 見 ru-cost-model-sizing
一般 Azure Database for PostgreSQL(single-node managed PG)就夠的條件:
- single-node 還沒到上限 — 多數 OLTP baseline 用 vertical scaling + read replica 就夠、不需要分散式
- 沒有好的 distribution column — 分散式 PostgreSQL 沒有均勻 distribution column 會 hot worker、好處拿不到、複雜度卻全付
- 不想承擔 distributed SQL 的複雜度(distribution column 設計、co-location 規劃、跨 shard query 成本)
判讀句:先確認 single-node PG 真的到上限、再確認 workload 是 SQL 範式(否則考慮 NoSQL)、最後確認有好的 distribution column。三個都成立、Cosmos DB for PostgreSQL 才是對的;缺任一個、回 single-node PG 或核心 Cosmos DB。
跟其他 distributed SQL 的位置
Cosmos DB for PostgreSQL 是 Azure 上、PostgreSQL-native、scale-out(co-location 設計驅動)的 distributed SQL。跟 Spanner(全球 external consistency、自己的 SQL 方言)、CockroachDB(跨雲、PostgreSQL wire、自動 range 分散)、Aurora DSQL(AWS、全球 active-active)位置不同:Cosmos DB for PostgreSQL 強在「真 PostgreSQL engine + extension 生態 + co-location 控制」、弱在它的分散需要 distribution column 設計(不像 CockroachDB / Spanner 自動分 range)、且綁 Azure。
操作流程
建叢集與設定 distribution column
1-- 建 distributed table、按 tenant_id 分片(多租戶 SaaS 典型)
2CREATE TABLE events (
3 tenant_id bigint NOT NULL,
4 event_id bigint NOT NULL,
5 payload jsonb,
6 created_at timestamptz DEFAULT now()
7);
8SELECT create_distributed_table('events', 'tenant_id');
9
10-- 維度小表設 reference table、讓 JOIN co-locate
11CREATE TABLE tenants (tenant_id bigint PRIMARY KEY, name text);
12SELECT create_reference_table('tenants');驗證:citus_tables 列出每張表的類型、distribution column 與 shard 數;同一段查詢帶不帶 distribution column 條件,EXPLAIN 的 Task Count 會從 1 變成 shard 總數。
1SELECT table_name, citus_table_type, distribution_column, shard_count
2FROM citus_tables;
3-- events | distributed | tenant_id | 32 ← 預設切成 32 個 shard
4-- tenants | reference | <none> | 1 ← reference table 只有一份,複製到每個 worker
5
6-- 帶 distribution column 條件:路由到單一 shard
7EXPLAIN (COSTS OFF) SELECT * FROM events WHERE tenant_id = 3;
8-- Custom Scan (Citus Adaptive)
9-- Task Count: 1
10
11-- 不帶 distribution column 條件:fan-out 到每一個 shard
12EXPLAIN (COSTS OFF) SELECT * FROM events WHERE event_id = 42;
13-- Custom Scan (Citus Adaptive)
14-- Task Count: 32驗證 co-location
1-- 第二張大表也用 tenant_id 分片,Citus 會把它放進 events 的 colocation group
2CREATE TABLE event_tags (tenant_id bigint NOT NULL, event_id bigint NOT NULL, tag text);
3SELECT create_distributed_table('event_tags', 'tenant_id');
4
5SELECT table_name, citus_table_type, distribution_column, colocation_id
6FROM citus_tables ORDER BY table_name;
7-- event_tags | distributed | tenant_id | 3 ← 與 events 同一個 colocation_id
8-- events | distributed | tenant_id | 3
9-- tenants | reference | <none> | 4 ← reference table 自成一組驗證:常一起 JOIN 的大表落在同一 colocation group、JOIN 在 worker 本地完成、不跨 worker shuffle。
加 worker 擴容
加 worker node 後 rebalance shard。驗證:rebalance 後 shard 在新舊 worker 間分布均勻、單一 worker 不再是 hot spot。
Rollback boundary
Cosmos DB for PostgreSQL 是叢集級服務、scale worker 是運維操作、可逆(縮回去)。distribution column 可以改,但代價是整張表重寫一次:Citus 的 alter_distributed_table 會建一張新表、把資料全部搬過去、再刪掉舊表,資料量越大越久。核心 Cosmos DB 的 partition key 則要自己建新 container 搬資料(見 partition-key-design)。兩者都讓選錯的代價跟資料量成正比,所以 distribution column 仍要在建表時選對。
1SELECT alter_distributed_table('events', distribution_column := 'event_id');
2-- NOTICE: creating a new table for public.events
3-- NOTICE: moving the data of public.events
4-- NOTICE: dropping the old public.events
5-- NOTICE: renaming the new table to public.events失敗模式
把它跟核心 Cosmos DB 當同一產品選
選型時把「Cosmos DB for PostgreSQL」當成「核心 Cosmos DB 的 PostgreSQL 介面」、規劃用 RU/s、找 consistency level 設定、結果整套 mental model 對不上 — 因為它是分散式 PostgreSQL、用 node 規格計費、用 PostgreSQL 的交易隔離級別。修法是選型第一步就確認「這是分散式 SQL、不是 NoSQL」、規劃按 PostgreSQL + Citus 的模型走、不要套核心 Cosmos DB 的概念。
沒有好的 distribution column 硬上分散式
workload 沒有均勻的 distribution column(例如資料天然集中在少數 tenant)、硬分片後變 hot worker、分散式的好處拿不到、複雜度全付。徵兆是少數 worker CPU / IO 飽和、其他 worker 閒置。修法是選型階段就評估 distribution column 的 cardinality 與均勻度;不均勻時、要嘛留 single-node PG(垂直擴 + read replica)、要嘛重新設計 distribution column(如多租戶用 composite 或對 hot tenant 特殊處理)。
大量跨 shard query / 非 co-located JOIN
application query 大多不帶 distribution column filter、或常做跨 distribution column 的 JOIN、每個 query fan-out 到所有 worker + shuffle、latency 與成本都差。徵兆是 EXPLAIN 顯示 query 打所有 worker、p99 latency 高。修法是重新設計 schema 讓常一起查的表 co-located、把 distribution column 放進熱 query 的 filter;改不動時、這個 workload 可能不適合 scale-out PG、回 single-node 或考慮其他方案。
該用 NoSQL 卻選了分散式 PG(或反之)
document / KV、固定 access pattern、不需要 JOIN 的 workload 選了 Cosmos DB for PostgreSQL、付了 SQL / distribution column 設計的複雜度卻沒用到關聯能力 — 這類 workload 核心 Cosmos DB(NoSQL)更自然。反過來、SQL / JOIN / 交易重的 workload 被推去核心 Cosmos DB(NoSQL)要重寫成 document model 也是錯。修法是回到「workload 是 SQL 範式還是 document / KV 範式」的根本判斷、見本文選型標準段與 mongodb-api-vs-sql-api 的範式判讀。
Anti-recommendation:single-node PG 沒到上限不要上
分散式 PostgreSQL 帶來 distribution column 設計、co-location 規劃、跨 shard query 成本、rebalance 運維。single-node managed PostgreSQL 加 vertical scaling 與 read replica 能撐的 OLTP baseline 比多數團隊以為的大。沒有觸及 single-node 真實上限(write throughput 飽和、單表大到 maintenance 困難、資料量超出單機)就上分散式、是用複雜度換不存在的容量需求。
容量與觀測
- 必看 metric:各 worker node 的 CPU / IO / 連線(找 hot worker)、shard 在 worker 間的分布均勻度、跨 shard query 比例、coordinator 連線數
- 容量單位:node 規格(不是 RU/s)— 規劃是 coordinator + N worker 的 vCPU / memory / storage、跟核心 Cosmos DB 的 RU 思維完全不同、不要混用 ru-cost-model-sizing 的 RU 模型來估這個服務
- distribution column 均勻度是容量上限的真實決定因素 — 跟 Hot Partition 同模型、hot worker 讓名義叢集容量達不到
- 回 9.6 容量規劃模型:scale-out 的有效容量 = node 數 × 單 node 容量 × distribution 均勻度
- Alert:單一 worker 飽和(distribution skew)、跨 shard query 比例上升、rebalance 後仍不均
邊界與整合
- 定位釐清:本服務是 分散式 PostgreSQL、不是核心 Cosmos DB(NoSQL)— 共用品牌名稱、產品不同、選型不要混淆
- 跟核心 Cosmos DB 的分界:SQL / JOIN / 交易 + 到單機上限 → 本服務;document / KV / multi-model / multi-region active-active → 核心 Cosmos DB、見 mongodb-api-vs-sql-api
- 跟 PostgreSQL vendor 的分界:single-node 沒到上限 → Azure Database for PostgreSQL / 一般 PG;PostgreSQL 既有的 Specialized PostgreSQL Variants 段已把 Cosmos DB for PostgreSQL 列為 Citus-based 變體之一
- 跟其他 distributed SQL:Spanner(全球強一致)、CockroachDB(跨雲、自動 range)— 本服務強在真 PostgreSQL engine + co-location 控制、弱在需 distribution column 設計 + 綁 Azure
- distribution column 改得動但要整張表重寫:跟 partition-key-design 的 partition key 一樣,選錯的代價跟資料量成正比
- Knowledge card:distributed SQL / Hot Partition
相關連結
- Cosmos DB vendor overview — Cosmos DB 其他深度文章的列表
- mongodb-api-vs-sql-api — SQL 範式 vs document / KV 範式的根本判讀
- PostgreSQL vendor / Specialized PostgreSQL Variants — single-node PG 與 Citus 變體定位
- Spanner vendor / CockroachDB vendor — 其他 distributed SQL 對照
- partition-key-design — 核心 Cosmos DB 的 partition key 怎麼選、選錯要付什麼代價
- Distributed SQL 卡片 / Hot Partition 卡片 — 概念基底
- 官方:Azure Cosmos DB for PostgreSQL / Citus distributed tables
#backend #database #cosmosdb #postgresql #citus #deep-article