CockroachDB 多區域指南:表本地性、延遲與容災取捨
CockroachDB 可以讓 SQL 資料庫跨區域運行,但多區域資料庫不是魔法。它可以提升韌性,並在資料靠近使用者時降低延遲;同時也可能帶來寫入延遲、交易衝突、維運複雜度和資料駐留設計問題。
這篇文章關注 CockroachDB 多區域設計中的實際選擇:應該加入哪些區域、如何選擇表本地性、什麼時候用 regional by row、什麼時候用 global table,以及生產前應該驗證什麼。
參考文件:
- CockroachDB multi-region overview
- Choosing a multi-region configuration
- Table localities
- Multi-region survival goals
最重要的設計問題
改 SQL 之前,先確認應用最需要什麼。
| 目標 | 設計方向 | 取捨 |
|---|---|---|
| 降低區域使用者讀取延遲 | 把資料放到主要讀取它的區域附近 | 跨區域寫入仍可能更慢 |
| 資料駐留 | 使用區域放置和明確的資料歸屬 | Schema 和路由必須嚴格 |
| 全球參考資料 | 對讀多寫少的小表使用 global table | 寫入通常需要更謹慎 |
| 區域故障容忍 | 選擇合適的 survival goal 和拓撲 | 更多副本和協調可能增加延遲 |
| 多區域活躍使用者 | 流量路由到近端區域,並建模資料歸屬 | 衝突寫入仍需應用處理 |
好的多區域設計從訪問模式出發,而不是只從地圖出發。
核心概念
CockroachDB 多區域抽象圍繞這些概念:
- Cluster region:節點啟動時透過 locality 設定的區域。
- Database region:加入到資料庫的區域,其中一個是 primary region。
- Primary region:資料庫預設使用的區域,除非表本地性另有設定。
- Table locality:CockroachDB 如何放置和優化表資料。
- Survival goal:資料庫設計上可承受的故障範圍。
- Secondary region:部分配置中用於故障切換相關的區域。
這些設定會影響效能和可用性,不只是部署標籤。
基礎多區域配置
多區域叢集從節點 locality 開始。每個節點應知道自己的 region 和 zone。
cockroach start \
--locality=region=us-east-1,zone=us-east-1a \
--certs-dir=certs \
--join=node1:26257,node2:26257,node3:26257
然後配置資料庫區域。
ALTER DATABASE global_app SET PRIMARY REGION "us-east-1";
ALTER DATABASE global_app ADD REGION "eu-west-1";
ALTER DATABASE global_app ADD REGION "ap-southeast-1";
SHOW REGIONS FROM DATABASE global_app;
這不會自動讓所有查詢在所有地方都變快。它只是建立區域基礎。使用者能感受到的延遲,更多取決於表本地性和應用路由。
選擇表本地性
表本地性決定資料如何放置和優化。
| Locality | 適合場景 | 說明 |
|---|---|---|
| Regional by table | 整張表主要屬於一個區域 | 適合區域專屬表 |
| Regional by row | 不同行屬於不同區域 | 適合使用者、租戶或帳戶資料 |
| Global | 小型讀多寫少參考資料 | 讀體驗好,寫入要更謹慎 |
Regional By Table
當整張表主要屬於一個區域時,使用 regional by table。
CREATE TABLE eu_tax_rules (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
country STRING NOT NULL,
rule JSONB NOT NULL
) LOCALITY REGIONAL BY TABLE IN "eu-west-1";
它適合區域配置、法律規則、本地目錄,或與某個地理區域綁定的營運資料。
Regional By Row
當每一行都有自己的 home region 時,使用 regional by row。使用者或租戶資料很常見。
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email STRING NOT NULL,
home_region crdb_internal_region NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
) LOCALITY REGIONAL BY ROW;
應用必須正確設定並維護 home region。不要把區域欄位當成普通附屬欄位,它是資料模型的一部分。
Global Tables
global table 適合各地都要讀、但很少寫的小表。
CREATE TABLE countries (
code STRING PRIMARY KEY,
name STRING NOT NULL
) LOCALITY GLOBAL;
適合國家代碼、很少變動的功能開關、公共配置和參考資料。不要把高頻交易寫入表預設設為 global,除非已經測試過延遲和衝突行為。
有意設計流量路由
資料庫放置只是設計的一半。應用流量通常也應路由到最近的健康區域。
需要檢查:
- 每個使用者請求由哪個應用區域處理。
- 連線池是否連線到本地資料庫節點。
- 讀取流量是否靠近使用者資料 home region。
- 寫入是否送到擁有該行或租戶的區域。
- 故障切換後,流量是否進入可以安全服務的區域。
如果歐洲使用者訪問歐洲應用伺服器,但該伺服器寫入 home 在美國的行,資料庫無法完全隱藏跨區域延遲。
處理交易衝突
多區域不會消除寫入歸屬設計。如果兩個區域頻繁更新同一行,仍可能出現重試和競爭。
降低衝突風險的方法:
- 為每個租戶、帳戶或文件指定清晰寫入歸屬。
- 避免熱點全域計數器。
- 使用冪等命令和可安全重試的交易邏輯。
- 縮短交易時間。
- 將高寫入記錄拆成更小的歸屬單元。
- 把讀多共享資料放進參考表。
Serializable 隔離很有價值,但應用仍然需要明確的重試策略。
資料駐留
如果有資料駐留要求,要明確哪些資料必須留在哪些區域:
- 使用者資料。
- 付款或帳單資料。
- 日誌和稽核紀錄。
- 備份。
- 派生分析資料。
- 搜尋索引和快取。
正確放置主表還不夠。副本、匯出、日誌、備份和下游系統也必須遵守同一策略。
Survival Goals
Survival goal 定義資料庫設計上要承受什麼故障。更高韌性通常需要更謹慎的拓撲,也可能影響延遲。
需要問:
- 需要承受 zone 故障,還是完整 region 故障?
- 有多少 region 和 zone 可用?
- 哪些表在故障時仍必須可寫?
- 目前 table locality 是否和相關特性相容?
- 是否用真實應用流量做過故障切換測試?
在沒有測試之前,不要在架構文件裡承諾固定 RTO 或 RPO。
生產檢查清單
上線前確認:
- 節點啟動時 region 和 zone 配置一致。
- 資料庫 primary region 和 secondary region 是有意設計的。
- 每張表按訪問模式選擇 locality。
- regional by row 表有可靠的區域賦值。
- 應用路由匹配資料歸屬。
- 交易重試邏輯已經實作。
- 備份和下游系統遵守駐留規則。
- Schema 變更從合適區域執行。
- 監控覆蓋延遲、重試、leaseholder 變化和 unavailable range。
- 故障演練有文件並且會重複執行。
常見錯誤
把所有表都設成 Global
Global table 有用,但不是預設選項。它適合小型讀多寫少資料。高寫入業務表必須謹慎測試。
忽略應用層
如果應用流量沒有按區域路由,資料庫本地性無法完全解決延遲。
缺少重試邏輯
分散式 SQL 系統需要應用處理可重試交易錯誤。重試應明確,並且操作要冪等。
把資料駐留當成單表設定
資料駐留包括備份、日誌、分析、搜尋和匯出。要稽核完整資料流。
不做故障測試
多區域設計應在節點、zone 和 region 故障場景下測試。不要假設架構圖等於生產行為。
總結
CockroachDB 多區域設計的核心是取捨:讓資料靠近使用者、滿足駐留要求、承受故障,並保持寫入路徑可理解。先從訪問模式和資料歸屬開始,再選擇表本地性、配置區域、實作重試並測試故障切換。
目標不是「所有地方都 active-active」,而是在正常流量和區域故障時都有可預期的行為。
本站提供瀏覽器本地工具,免註冊即可試用 →