ACE-213:Cloud Spanner 深度解析——全球分散式關聯資料庫完全指南
前言
Cloud SQL 夠用了嗎?如果你的應用要的是全球規模的關聯式資料庫,跨洲讀寫、無限水平擴展、99.999% 可用性,同時又得維持 ACID 交易和強一致性——這種情境,要嘛 Cloud SQL 撐到極限開始痛,要嘛你早該認識 Spanner 了。
讀這篇前,最好先懂 Cloud SQL 的侷限(系列前面的 GCP-108:Cloud SQL 講過):它是單區域寫入、靠垂直擴展加機器、跨區域只能開唯讀複本。當你撞到「全球都要能寫、又不能放掉強一致性」這道牆時,才會真正體會 Spanner 在解什麼問題。
這是 ACE 進階系列第 13 課,我們會把 Spanner 的架構設計和實際操作好好談一遍。
什麼是 Cloud Spanner?
Spanner 是 Google 自研的全球分散式關聯資料庫,同時具備:
傳統 SQL 資料庫的能力 + NoSQL 的水平擴展
├── ACID 交易 ├── 自動分片(Sharding)
├── SQL 查詢 ├── 無限水平擴展
├── 強一致性 ├── 自動負載均衡
└── Schema + Foreign Key └── 跨區域複製
Spanner 厲害在它同時做到外部一致性(External Consistency)跟全球水平擴展——這組合在業界非常罕見。Google 自己的 Google Ads、Google Play、Google Photos 這些核心服務,背後跑的就是它。
核心架構
Processing Units(PU)
Spanner 的運算單位是 Processing Unit(PU):
| 規格 | 說明 |
|---|---|
| 最小單位 | 100 PU |
| 1 Node | 1,000 PU |
| 增量 | 100 PU 為單位 |
| 每 100 PU 儲存 | 1,024 GiB |
| 每 Node 儲存 | 10 TiB |
# 建立 Spanner 實例(100 PU,最小規模)
gcloud spanner instances create my-instance \
--config=regional-asia-east1 \
--processing-units=100 \
--description="Development instance"
# 建立 1 Node 實例
gcloud spanner instances create prod-instance \
--config=regional-asia-east1 \
--nodes=1 \
--description="Production instance"
資料分片(Split)
Spanner 會自動依主鍵範圍把資料切到多個分片(Split),每個分片交給不同的伺服器處理:
表格 Users(主鍵:userId)
Split 1: userId A-M → 伺服器 1
Split 2: userId N-Z → 伺服器 2
(隨資料增長自動再分裂)
主鍵設計——最關鍵的決策
避免單調遞增主鍵
這是 Spanner 設計裡最重要的一條原則。拿 AUTO_INCREMENT 或時間戳當主鍵,會讓所有寫入都擠到同一個分片上,也就是所謂的熱點:
-- ❌ 錯誤:單調遞增,造成寫入熱點
CREATE TABLE Orders (
orderId INT64 NOT NULL, -- 1, 2, 3, 4...
...
) PRIMARY KEY (orderId);
-- ✅ 正確:使用 UUID,寫入均勻分散
CREATE TABLE Orders (
orderId STRING(36) NOT NULL, -- UUID
...
) PRIMARY KEY (orderId);
-- ✅ 正確:使用 GENERATE_UUID()
CREATE TABLE Orders (
orderId STRING(36) NOT NULL DEFAULT (GENERATE_UUID()),
...
) PRIMARY KEY (orderId);
-- ✅ 正確:反轉位元序列,打散熱點
CREATE SEQUENCE EventIdSeq OPTIONS (sequence_kind='bit_reversed_positive');
CREATE TABLE Events (
eventId INT64 NOT NULL DEFAULT (GET_NEXT_SEQUENCE_VALUE(SEQUENCE EventIdSeq)),
...
) PRIMARY KEY (eventId);
📝 考場提點
主鍵熱點(Hotspot)是 ACE 最愛考 Spanner 的點,幾乎是必考題。題目會給你一個用
AUTO_INCREMENT、自增 ID 或時間戳當主鍵的設計,問你「為什麼寫入效能不好」——答案永遠是:主鍵單調遞增,所有新寫入都落到同一個分片(Split),變成寫入熱點,水平擴展白做工。看到這些關鍵字就往「打散主鍵」想:單調遞增(monotonically increasing)、寫入熱點、均勻分散(evenly distribute)。三種正解的取捨:
- UUID /
GENERATE_UUID():最常見、最好記,隨機分散;缺點是字串較長、沒有時間順序。- bit-reversed sequence(位元反轉序列):想要整數主鍵、又要打散時用;把連續整數的位元反轉,相鄰值就被甩到不同分片。
- 反過來,需要照時間排序又怕熱點時,可以把高基數欄位放主鍵前綴(例如
userId在前、時間戳在後),用前綴先打散。一句話:選型題看到「自增主鍵 + 寫入慢」就是熱點,要的答案是「換成均勻分散的主鍵」。
Interleaved Tables(交錯表)
這是 Spanner 獨有的功能,把父子表的資料實際存在一起,JOIN 效能會明顯變好:
-- 父表
CREATE TABLE Customers (
customerId STRING(36) NOT NULL,
name STRING(100),
email STRING(200)
) PRIMARY KEY (customerId);
-- 子表(交錯在父表中)
CREATE TABLE Orders (
customerId STRING(36) NOT NULL,
orderId STRING(36) NOT NULL,
amount FLOAT64,
createdAt TIMESTAMP
) PRIMARY KEY (customerId, orderId),
INTERLEAVE IN PARENT Customers ON DELETE CASCADE;
-- 孫表(可以多層交錯)
CREATE TABLE OrderItems (
customerId STRING(36) NOT NULL,
orderId STRING(36) NOT NULL,
itemId STRING(36) NOT NULL,
productName STRING(200)
) PRIMARY KEY (customerId, orderId, itemId),
INTERLEAVE IN PARENT Orders ON DELETE CASCADE;
效能差異
一般 JOIN(非交錯):可能跨多個分片 → 網路延遲
Interleaved JOIN:資料在同一個分片 → 本地讀取
查詢 "Customer X 的所有訂單和商品"
非交錯:3 次跨分片請求
交錯:1 次本地讀取
SQL 方言:GoogleSQL vs PostgreSQL
Spanner 支援兩種 SQL 方言:
| 特性 | GoogleSQL | PostgreSQL |
|---|---|---|
| 狀態 | 原生預設 | GA(2022 年) |
| 語法 | Spanner 專屬 | PostgreSQL 相容 |
| 驅動 | Spanner 客戶端 | 標準 PG 驅動(psycopg2 等) |
| Interleaved Tables | 支援 | 支援 |
| Graph | 支援 | 不支援 |
| 遷移 | 需要學習 | 從 PostgreSQL 遷移更容易 |
# 建立 PostgreSQL 方言資料庫
gcloud spanner databases create my-db \
--instance=my-instance \
--database-dialect=POSTGRESQL
# 建立 GoogleSQL 方言資料庫(預設)
gcloud spanner databases create my-db \
--instance=my-instance \
--database-dialect=GOOGLE_STANDARD_SQL
標準 PG 驅動(像 psycopg2)沒辦法直連 Spanner,得透過 PGAdapter(PostgreSQL wire-protocol 代理 / sidecar):驅動先連到本機的 PGAdapter,再由它翻譯成 Spanner gRPC。
# 先啟動 PGAdapter 代理(PostgreSQL wire-protocol proxy / sidecar)
# docker run -d -p 5432:5432 gcr.io/cloud-spanner-pg-adapter/pgadapter \
# -p my-project -i my-instance
# 標準 PG 驅動連到本機 PGAdapter,由 PGAdapter 翻譯成 Spanner gRPC
import psycopg2
conn = psycopg2.connect(
database="my-db", # PostgreSQL 方言資料庫名稱
host="localhost", # PGAdapter 監聽位址(非 Spanner 資源路徑)
port=5432
)
多區域配置
區域類型與 SLA
| 配置類型 | SLA | 適合場景 |
|---|---|---|
| Regional | 99.99% | 單一區域,低延遲 |
| Dual-Region | 99.999% | 資料駐留需求 |
| Multi-Region | 99.999% | 全球高可用 |
常見多區域配置
| 名稱 | 區域 | 涵蓋範圍 |
|---|---|---|
| nam-eur-asia1 | Iowa + Oklahoma + Belgium + Taiwan | 三洲 |
| nam6 | 讀寫:Iowa + South Carolina;唯讀:Oregon + LA;witness:Oklahoma | 北美 |
| eur3 | Belgium + Netherlands | 歐洲 |
| asia1 | Tokyo + Osaka | 亞洲 |
Dual-Region(Enterprise Plus 專屬)
dual-region-japan1: Tokyo + Osaka
dual-region-india1: Mumbai + Delhi
dual-region-germany1: Berlin + Frankfurt
Dual-Region 配置只在 Enterprise Plus 版本提供,每個 dual-region 包含 6 個副本(每區域 3 個)。
交易與一致性
External Consistency
Spanner 提供的是外部一致性,這是比強一致性更嚴格的保證。簡單講,就是只要 T1 先提交,全世界任何地方的 T2 都保證讀得到——不會出現 A 洲已經寫好、B 洲還讀到舊值的情況。把這個白話畫面記住,再看下面的時間點示意就很好懂:
交易 T1 在時間點 t1 提交
交易 T2 在時間點 t2 開始(t2 > t1)
外部一致性保證:T2 一定能看到 T1 的結果
(即使 T1 和 T2 在不同大陸的不同伺服器上)
交易類型
from google.cloud import spanner
instance = spanner.Client().instance("my-instance")
database = instance.database("my-db")
# 讀寫交易(鎖定)
def transfer(transaction):
row_a = transaction.read("Accounts", ["balance"],
keyset=spanner.KeySet(keys=[[1]]))
row_b = transaction.read("Accounts", ["balance"],
keyset=spanner.KeySet(keys=[[2]]))
# ... 更新餘額
database.run_in_transaction(transfer)
# 唯讀交易(不鎖定,用快照)
with database.snapshot() as snapshot:
results = snapshot.execute_sql(
"SELECT * FROM Users WHERE age > @min_age",
params={"min_age": 25},
param_types={"min_age": spanner.param_types.INT64}
)
Change Streams
Spanner 的 Change Streams(GA,2022 年)可以讓你追蹤資料變更:
-- 建立 Change Stream,監控所有表
CREATE CHANGE STREAM AllChanges FOR ALL;
-- 只監控特定表和欄位
CREATE CHANGE STREAM OrderChanges
FOR Orders(orderId, amount, status);
常見用途
- 即時同步到 BigQuery 做分析
- 觸發 Pub/Sub 訊息做事件驅動
- 歸檔變更到 Cloud Storage 做合規
重要限制
- 資料保留期:1-7 天(預設 7 天)
- 不追蹤 schema 變更、索引、views
- 會增加儲存和複製成本
Spanner Editions
| 特性 | Standard | Enterprise | Enterprise Plus |
|---|---|---|---|
| 區域配置 | Regional | Regional | Regional + 多區域 + Dual-Region |
| SLA | 99.99% | 99.99% | 最高 99.999% |
| Managed Autoscaler | 不支援 | 支援 | 支援 |
| Spanner Graph | 不支援 | 支援 | 支援 |
| 向量搜尋 | 不支援 | 支援 | 支援 |
| 全文搜尋 | 不支援 | 支援 | 支援 |
| Columnar Engine | 不支援 | 支援 | 支援 |
| Geo-Partitioning | 不支援 | 不支援 | 支援 |
| CUD 折扣 | 1 年 20% / 3 年 40% | 1 年 20% / 3 年 40% | 1 年 20% / 3 年 40% |
備份與還原
# 建立備份(保留 30 天)
gcloud spanner backups create my-backup \
--instance=my-instance \
--database=my-db \
--retention-period=30d \
--async
# 從備份還原
gcloud spanner databases restore \
--destination-instance=my-instance \
--destination-database=my-db-restored \
--source-instance=my-instance \
--source-backup=my-backup
# 匯出到 GCS(透過 Dataflow)
gcloud dataflow jobs run export-spanner \
--gcs-location=gs://dataflow-templates/latest/Cloud_Spanner_to_GCS_Avro \
--parameters=instanceId=my-instance,databaseId=my-db,outputDir=gs://my-bucket/export/
免費試用
Spanner 提供 90 天免費試用實例:
| 項目 | 限制 |
|---|---|
| 試用期 | 90 天 |
| 儲存空間 | 10 GB |
| 最多資料庫 | 5 個 |
| 每個帳單帳號 | 最多 5 個試用實例 |
| SLA | 無 |
| 備份 | 不支援 |
| 可用區域 | 僅 Regional |
| 到期後 | 30 天寬限期,之後刪除 |
# 建立免費試用實例
gcloud spanner instances create free-instance \
--config=regional-us-central1 \
--processing-units=100 \
--instance-type=free-instance
定價概覽
Spanner 依運算 + 儲存 + 網路計費:
| 項目 | 費用(Regional,美國) |
|---|---|
| 運算(Standard) | ~$0.65 / Node-hour |
| 運算(Enterprise) | ~$0.90 / Node-hour |
| 儲存 | $0.30 / GB / 月 |
| 備份儲存 | $0.10 / GB / 月 |
| 多區域運算 | 約 3x Regional 價格 |
最小運算費用是 100 PU(= 0.1 Node),大約 $65-90/月起。
Cloud Spanner vs Cloud SQL vs AlloyDB
ACE 考試高頻選型題:
| 特性 | Cloud SQL | AlloyDB | Cloud Spanner |
|---|---|---|---|
| 類型 | 托管 MySQL/PostgreSQL | 托管 PostgreSQL | 全球分散式 |
| 擴展 | 垂直 + 讀取複本 | 垂直 + 讀取複本 | 水平擴展 |
| 最大儲存 | 64 TB | 128 TiB | 無限(加 Node) |
| 跨區域 | 跨區域讀取複本 | 跨區域讀取複本 | 跨區域讀寫 |
| SLA | 99.95%(Enterprise)/ 99.99%(Enterprise Plus) | 99.99% | 99.999% |
| SQL 相容 | 完全相容 | 完全 PG 相容 | 部分相容 |
| 價格 | 最低 ~$7/月 | ~$100/月起 | ~$65/月起 |
| 最適合 | 中小型 OLTP | 高效能 PG | 全球規模 OLTP |
選型公式
單一區域、中小規模、傳統 SQL?
→ Cloud SQL ✅
需要高效能 PostgreSQL、AI/ML 整合?
→ AlloyDB ✅
全球規模、跨區域寫入、99.999% 可用性?
→ Cloud Spanner ✅
不需要 SQL、Mobile/Web 即時同步?
→ Firestore ✅
📝 考場提點
資料庫選型是 ACE 整張考卷的高頻題型,Spanner 的判定條件其實很集中。題目只要同時出現「全球跨區域讀寫+ACID 交易+99.999% 可用性」,答案就鎖 Spanner,別猶豫。常見英文關鍵字:globally distributed、horizontal scale(水平擴展)、strong / external consistency、cross-region writes(跨區域寫入)。
容易被拿來當干擾選項的,記住它們的「破綻」就好:
- Cloud SQL:只能單區域寫入,跨區域那台是唯讀複本——題目一出現「多個區域都要能寫」就出局。
- Firestore:是 NoSQL、強項是 Mobile/Web 的即時同步——題目要「關聯式 / SQL / 強一致交易」就不是它。
- AlloyDB / Cloud SQL 也能跨區域,但只跨在「讀」,不是「寫」。
一句話:要「跨區域同時寫、又要強一致」只有 Spanner;其餘情境先想便宜的 Cloud SQL。
ACE 考試重點整理
必背知識點
- Spanner = 全球分散式 + ACID + SQL + 水平擴展,全世界獨一無二
- 避免單調遞增主鍵(最常考的設計原則)——用 UUID 或 BIT_REVERSE
- Interleaved Tables 將父子資料物理共置,大幅提升 JOIN 效能
- 1 Node = 1,000 PU,最小 100 PU,每 Node 10 TiB 儲存
- Regional 99.99%,Multi-Region 99.999%
- Enterprise Plus 才支援多區域和 dual-region
- Change Streams 可即時追蹤資料變更到 BigQuery 或 Pub/Sub
常見陷阱題
Q:需要全球多區域讀寫、ACID 交易的資料庫,選什麼? A:Cloud Spanner。Cloud SQL 只能單區域寫入,讀取複本是唯讀的。
Q:Spanner 使用 AUTO_INCREMENT 作為主鍵會有什麼問題? A:造成寫入熱點(Hotspot)。所有新寫入都集中在最後一個分片,無法利用 Spanner 的水平擴展優勢。
Q:Spanner 的 99.999% SLA 需要什麼配置? A:Enterprise Plus 版本 + 多區域配置。Regional 只有 99.99%。
Q:想在 Spanner 中儲存父子關聯資料並高效查詢,該用什麼功能? A:Interleaved Tables。它讓父子資料物理上存放在同一個分片,避免跨分片 JOIN。
一句話記住 Spanner
當你需要「全球都能寫、還要 ACID 跟強一致性」時,市面上幾乎沒有對手——這就是 Spanner 的定位。其他情況先想 Cloud SQL,省錢又夠用。上面的必背知識點和選型提點掃過一遍,ACE 考這塊的分數基本就拿穩了。
下一課 GCP-113:Cloud Bigtable 入門,來看 PB 級寬列 NoSQL 資料庫怎麼設計、怎麼操作。
資料庫選型系列
| 課程 | 服務 | 適合場景 |
|---|---|---|
| GCP-108 | Cloud SQL | 中小型 OLTP、傳統 SQL 應用 |
| GCP-112 | Firestore | Mobile/Web App、即時同步 |
| GCP-113 | Bigtable | PB 級 IoT/時序、低延遲高吞吐 |
| ACE-211 | BigQuery | PB 級分析、BI 報表 |
| 本課 ACE-213 | Spanner | 全球規模 OLTP、強一致性 |
| GCP-115 | Memorystore | 微秒級快取、Session 管理 |
📖 完整比較:想一次看懂所有資料庫差異?參考 GCP 資料庫選型完全指南。