資料分片 (Sharding)

一句話定位

Sharding 是「單台 DB 撐不住時」把資料拆散到多台機器的做法。提升儲存容量和吞吐量,但代價是巨大的複雜度

Partitioning vs Sharding

範圍 例子
Partitioning 單一 DB instance 內部拆分 PostgreSQL table partition
Sharding 拆到多台機器 MongoDB / Cassandra cluster

實務上很多人混用,重點是說清楚資料在一台還是多台

Partitioning 兩種

類型 怎麼拆
水平分區(Horizontal) 拆 row 每年訂單一個分區
垂直分區(Vertical) 拆 column 常用欄位一區、大欄位另一區

Sharding 是「水平分區延伸到多台機器」。


Shard Key 的三條件

條件 為什麼 反例
高基數(High Cardinality) 否則 shard 數受限 is_premium(布林只能分 2 個)
均勻分佈(Even Distribution) 否則熱點 country 分片但 90% 用戶在美國
契合查詢模式 否則 query 必須打所有 shard user_id 分片,多數 query 限定單一用戶

Good vs Bad 範例

🟢 user_id 用於用戶導向 App

🟢 order_id 用於電商訂單

🔴 is_premium(布林)

🔴 成長中表用 created_at


三種分片策略

1. Range-Based Sharding(範圍分片)

依連續值範圍分組。

Shard 1 → user_id 1 ~ 1M
Shard 2 → user_id 1M ~ 2M
Shard 3 → user_id 2M ~ 3M

2. Hash-Based Sharding(雜湊分片)— 預設首選

shard = hash(user_id) % N

User 42  → hash(42) % 4 = Shard 2
User 99  → hash(99) % 4 = Shard 3
User 123 → hash(123) % 4 = Shard 1

Consistent Hashing 在增減節點時把資料移動量降到最低(K/N),而不是 hash % N 的「幾乎全搬」。

面試預設:除非特別說明,hash sharding 是預設答案。

3. Directory-Based Sharding(目錄分片)

查找表存「哪筆對應哪個 shard」。

user_to_shard:
  User 15  → Shard 1
  User 87  → Shard 4
  User 204 → Shard 2
在面試中目錄分片幾乎不是正確答案

增加 SPOF 和延遲,面試官會追問一連串會讓對話偏離主題的問題。除非題目明確需要動態靈活性才提。


Sharding 的三大挑戰

挑戰 1:熱點與負載不均(Celebrity Problem)

就算 shard key 選得好,有些 key 本來就特別熱

user_id 分片,Taylor Swift 的 shard 流量是普通用戶的 1000 倍 —— 雜湊函數對她一視同仁,但她本身就是熱資料。

偵測:監控各 shard 的 query latency、CPU、QPS,發現某 shard 持續偏高即熱點。

解法

  1. 熱 key 隔離到專屬 shard(這時 directory sharding 有用)
  2. 複合 shard keyhash(user_id + date) —— 同一用戶的資料按時間分散
  3. 動態 shard 拆分:MongoDB balancer / Vitess online resharding

挑戰 2:跨 Shard 操作

查詢模式和 shard key 不對齊時,必須打所有 shard、等所有回應、再聚合。

user_id 分片 → 「全站最熱門 10 篇貼文」要打 64 個 shard,64 倍的網路 + latency

最小化方式

設計訊號

如果你發現自己對常見場景說「查所有 shard 然後聚合」,暫停一下 —— 可能是 shard key 選錯或邊界劃分有問題。

挑戰 3:跨 Shard 一致性

單一 DB 的 transaction 失效。

解法(從好到差)

  1. 設計成完全避免跨 shard transaction:把同一用戶的所有資料放同一個 shard(用 user_id 分片時最自然)
  2. 真的避不掉 → 用 Saga 模式(見 05-Database-Advanced/01-Transactions
  3. 接受最終一致性:粉絲數、計數類資料反正規化在多個 shard,幾秒不一致無妨
  4. 2PC:教科書答案,生產環境通常避免(效能與脆弱性代價過高)
設計訊號(再次)

一直需要分散式 transaction → shard key 選錯了


現代資料庫的 Sharding

好消息:你大概不會從零實作 sharding。

資料庫 機制
Cassandra Partitioner(Murmur3)+ virtual nodes,consistent hashing 變體
DynamoDB 對 partition key 雜湊,自動拆分/合併分區(內部,不暴露 ring)
MongoDB 依 shard key 切 chunk(範圍 or 雜湊),balancer 自動搬移
Vitess / Citus 架在 MySQL/PostgreSQL 前的開源 sharding 層
Aurora / Cloud Spanner 雲端內建分散式 SQL
面試話術

「我們用 DynamoDB 以 user_id 作為 partition key
或「Vitess 以 user_id 分片,需要擴展時由操作人員發起 online resharding」
—— 通常就夠。除非面試官追問,不必細到實作層。


系統設計面試怎麼談 Sharding

時機(最常見錯誤:過早分片)

不要在還沒證明必要性之前引入 sharding

500GB / 1-2TB 就喊 sharding 是常見錯誤。Sharding 之前的優化階梯:
慢查詢 → 加 index → 加 cache → 加 read replica → 調 DB 參數 → 升級硬體 → 才談 sharding

帶出 sharding 的觸發訊號:

觸發點 數字(參考 02-Distributed-Systems/05-Numbers-to-Know
儲存空間 逼近 50TiB
寫入吞吐 長期 > 10k TPS
讀取吞吐 read replica 也應付不了
跨區域 需要多 region

四步驟說明(用社群媒體為例)

Step 1:根據存取模式選 shard key

「社群媒體查詢多是用戶為中心:feed、追蹤、按讚都限定單一用戶。我用 user_id 分片。」

Step 2:選分佈策略

「Hash sharding 配 consistent hashing,把用戶均勻分散。」

Step 3:點出取捨

「全域查詢(如全站熱門貼文)會貴。用快取熱門內容 + 背景任務預計算來解決。」

Step 4:規劃成長

「從 64 個 shard 起步留充裕空間。Consistent hashing 讓未來增 shard 只搬一小部分資料。」