當資料量或查詢吞吐超過單節點負荷,就把資料拆成子集分散到多台機器。目標是讓資料與負載均勻分佈、避免熱點 (hot spot)。分片與複製通常搭配使用——每筆資料屬於一個分片,該分片再複製到多個節點以容錯。
🧠 Core Ideas
TIP
兩種基本分片策略,是「範圍查詢」與「負載均勻」之間的取捨:按鍵範圍保留排序、範圍掃描快,但容易熱點;按雜湊值打散得均勻,但失去範圍查詢。Cassandra 的複合主鍵是折衷——第一段雜湊決定分片,其餘段在分片內排序。
- 按鍵範圍 (key range):每個分片一段連續鍵,邊界依實際資料量調整。Bigtable、HBase 用。時間戳當鍵是經典反例——寫入全擠到「今天」。
- 按雜湊值 (hash):雜湊函數把相近的鍵也打散。代價是範圍查詢得送往所有分片。別用語言內建 hash(如 Java
hashCode()跨 process 不穩定)。 - 名詞提醒:DDIA 建議直接叫「雜湊分片」,因為 consistent hashing 原指 CDN 的負載分散法,對資料庫並不好用。
⚖️ 次要索引與分片
主鍵分片很單純,麻煩的是次要索引(查「所有紅色的車」這種)無法自然對齊分片結構:
按文件分片(本地索引)— 寫入快、讀取 scatter/gather
- 每個分片各自維護只涵蓋自己文件的索引,寫入只更新該分片 → 本地索引。
- 讀取麻煩:目標值可能散在任何分片,得向所有分片查再彙整= scatter/gather,受尾端延遲放大影響。
- 實務最常見:MongoDB、Cassandra、Elasticsearch、SolrCloud 都用這套。
按詞彙分片(全域索引)— 讀取快、寫入慢且複雜
- 建跨所有分片的全域索引,索引本身也分片,但依「詞彙」而非主鍵分。
- 讀取只查含目標詞彙的那個索引分片,免 scatter/gather。
- 但一筆文件的多個詞彙可能落在不同索引分片 → 寫入要更新多個分片,通常非同步(如 DynamoDB 全域次要索引)。
⚖️ 重新平衡 (Rebalancing)
節點增減、資料成長時要搬資料。原則:搬完負載公平、過程中持續讀寫、搬動量盡可能少。
錯誤示範:hash mod N
- 直覺想用
hash(key) mod N,但 N 一改,幾乎所有鍵都要搬。完全不可行。 - (這正是一致性雜湊要解決的問題。)
固定分片數 — 分片數不變,只改分片到節點的分配
- 一開始建遠多於節點的分片(如 10 節點建 1000 分片),每節點分一批。
- 加節點就從現有節點「偷」幾個分片;鍵到分片的對應不變。Riak、Elasticsearch、Couchbase 用。
- 挑戰:初始化就得選好分片數,太少限制擴展、太多管理開銷大。
動態分片 — 分片隨資料量自動分裂 / 合併
- 分片超過閾值(HBase 預設 10 GB)自動分裂,縮小則與相鄰合併。
- 適合按鍵範圍。空庫只有一個分片會集中寫 → 用預分片 (pre-splitting) 解。
按節點比例分片 — 每節點固定數量分片
- Cassandra 預設每節點 256 分片。加節點時隨機分裂現有分片、取走一半。
- 需搭配雜湊分片(分裂邊界從雜湊範圍隨機挑)。
WARNING
全自動重新平衡 + 自動故障偵測很危險:某節點暫時過載回應慢,被誤判失效而觸發再平衡,反而把更多負載壓上去 → 連鎖故障。讓人參與決策(半自動)是有價值的。
⚖️ 請求路由 (Service Discovery)
資料分散後,客戶端怎麼知道連哪個節點?三種做法:
三種路由方式
- 任意節點轉發:客戶端連任一節點,命中就處理、否則轉發(Cassandra / Riak 用 gossip protocol)。
- 路由層:請求先過 routing tier 做分片感知的負載平衡(MongoDB 的 mongos)。
- 客戶端感知:客戶端自備分片→節點對應,直連正確節點。
- 三者共同難題:對應關係變化時,誰、如何得知?許多系統用 ZooKeeper 維護權威對應並通知訂閱者(HBase、SolrCloud、Kafka)。
🔑 Takeaways
- 分片核心是均勻分佈、避免熱點;設計原則是讓每個分片盡量獨立運作——這是水平擴展的基礎。
- 策略選型=範圍查詢 vs 負載均勻;要兩者兼得看 Cassandra 複合主鍵。
- 次要索引要先想清楚:本地索引(寫快讀慢)還是全域索引(讀快寫慢)。
hash mod N是反面教材;再平衡用固定分片數 / 動態分片 / 按節點比例。- 跨分片寫入(一次成功一次失敗怎麼辦)是後續分散式交易的主題。
No notes yet — jot your takeaways or Q&A here.