🎯 什麼情境該想到我
當單一機器裝不下資料/扛不住流量,需要把資料切分到多台機器(水平擴展)時。
⚙️ 怎麼用
- 選分區鍵與策略:
- 按範圍分區:範圍查詢方便,但易產生熱點(如全部照時間寫入同一分區)。
- 按雜湊分區:分佈均勻、避熱點,但失去範圍查詢能力。
- 處理熱點:熱鍵可加隨機前綴打散。
- 次級索引:本地索引(分區內,查詢要 scatter/gather)vs 全域索引(寫入要跨分區)。
- 再平衡(rebalancing):用固定數量分區等策略,避免資料大搬風。
★ 分區鍵的選擇(這一步決定成敗,而且極難反悔)
| 策略 | 做法 | 優點 | ★ 致命弱點 |
|---|---|---|---|
| 按範圍 | 依 key 的區間切(a–f、g–m) | ★ 範圍查詢快(同一分區內) | ★ 熱點:時間序列全部寫進最新的那一片 |
| 按雜湊 | hash(key) % n 決定分區 | ★ 分佈均勻 | ★ 失去範圍查詢;% n 讓加節點要搬幾乎所有資料 |
| ★ 一致性雜湊 | 雜湊到環上,節點也在環上 | ★ 加減節點只搬 1/n 的資料 | 實作較複雜(見 工具-一致性雜湊) |
| 複合鍵 | (tenant_id, timestamp) | ★ 兼顧兩者:租戶內可範圍查詢,跨租戶均勻 | 要選對主序 |
★ 實務上最常見的正確答案是「複合鍵」:
高基數的欄位當分區鍵(決定去哪一片),低基數/有序的當排序鍵(決定片內的順序)。
Cassandra 的PRIMARY KEY ((tenant_id), created_at)就是這個形狀。
★ 熱點的三種成因與對策
| 成因 | 例子 | 對策 |
|---|---|---|
| 時間遞增的 key | 用 created_at 或自增 ID 當分區鍵 | ★ 改用雜湊或複合鍵;或反轉時間戳的位元 |
| ★ 名人問題(celebrity) | 某個大客戶佔 40% 流量 | ★ 加隨機前綴打散:key → {rand(0..9)}#{key},讀的時候要查 10 片再合併 |
| 存取偏斜 | 少數熱門商品 | 快取擋在前面(見 工具-快取與資源優化)——比分區便宜得多 |
★ 加隨機前綴是有代價的:寫入分散了,但每次讀都要 scatter-gather。
只對確定的熱鍵做,不要全表都加。
★ 次級索引:兩種都有嚴重缺點
① 本地索引(document-partitioned)
每個分區維護「自己那份資料」的索引
✅ 寫入只碰一個分區(快)
❌ ★ 查詢要問「所有」分區再合併(scatter-gather)
→ 延遲 = 最慢的那一片;分區越多尾延遲越糟
② 全域索引(term-partitioned)
索引本身也被分區(依索引的 term 分)
✅ 查詢只問一個分區(快)
❌ ★ 寫入一筆資料要更新「多個」分區的索引
→ 需要分散式交易,否則索引會與資料不一致(多半是非同步更新 → 最終一致)
★ 這是分區系統無法迴避的取捨:寫快讀慢,或讀快寫慢。
多數系統選 ①(本地索引 + scatter-gather),因為寫入路徑的簡單性更重要。
★ 再平衡:為什麼「固定數量的分區」是主流
❌ hash(key) % n
n 從 3 變 4 → ★ 幾乎所有 key 的歸屬都變了 → 全量資料搬遷
✅ 固定數量的邏輯分區(例如一開始就切 1024 片)
節點數變化時,只是「哪些分區歸哪個節點」改變
★ 搬的是「整片」,數量可控,而且分區鍵的計算永遠不變
★ 這是 Kafka(partition 數)、Elasticsearch(shard 數)、Riak 的做法。
代價:分區數一開始就要定好,之後很難改(Kafka 可以加但不能減,而且加了會破壞 key 的順序保證)。
動態分區(HBase、MongoDB)則是「太大就分裂、太小就合併」——彈性好,但再平衡的時機不可控。
★ 請求路由的三種做法
| 做法 | 例子 |
|---|---|
| 客戶端自己算(胖客戶端) | Cassandra 的 driver |
| 中間的路由層 | ★ ProxySQL、Vitess、mongos |
| 任一節點都能轉發 | Cassandra、Riak(gossip 同步拓撲,見 工具-反熵與Gossip傳播) |
🧪 我實際套用的紀錄
- 2026-07-15:(待填)
⚠️ 注意 / 什麼時候不適用
- ★★ 先別分區。 單機 PostgreSQL 撐到幾 TB、幾萬 QPS 是常態。
分區帶來的複雜度(跨分區 JOIN、分散式交易、再平衡、運維)遠超過多數團隊的預期。
先做:加索引、加快取、加讀取副本、垂直擴展、歸檔舊資料。 - ★ 分區鍵幾乎不可能改。改分區鍵 = 全量資料遷移 + 應用改寫。這是你會做的最難反悔的決定之一。
- ★ 跨分區的操作全部變貴:JOIN、交易、
ORDER BY+LIMIT、COUNT(*)、唯一性約束。
→ schema 設計要讓「一起查的資料落在同一分區」(這是 工具-限界上下文 與聚合設計的實際價值)。 - 分區 ≠ 複製。分區解決「容量」,複製解決「可用性與讀取吞吐」——兩者正交,通常要一起做(見 工具-資料複製)。
- ★ 唯一性約束跨分區時會失效。全域唯一的 email 需要額外的機制(獨立的唯一性表、或把 email 當分區鍵)。
- 監控要 per-partition。整體平均看起來很好,但某一片已經爆了——熱點只有分片層級的指標看得出來。
- 再平衡不要全自動。自動再平衡在節點暫時不可用時可能觸發連鎖搬遷 → 雪崩。要有人工確認的閘門。
🔗 相關工具
- 工具-一致性雜湊 —— 再平衡時搬最少資料
- 工具-資料複製 —— 正交的另一半
- 工具-快取與資源優化 —— ★ 比分區便宜得多的第一選擇
- 工具-限界上下文 —— 讓「一起查的資料」落在同一分區
- 雜湊表 —— 分區與雜湊的共同基礎
- 工具-容量估算 —— 決定要不要分區的依據
- 工具-反熵與Gossip傳播 —— 節點怎麼知道拓撲
- 設計資料密集型應用