單台資料庫的瓶頸
你的 App 剛上線時,一台 PostgreSQL 就夠用了。但當用戶從 10 萬成長到 1000 萬, 你開始發現資料庫成為整個系統的瓶頸。問題不是 Query 寫得不好,而是單台機器本身的物理極限。
面對這些瓶頸,工程師通常有兩條路可以走:
垂直擴展(Scale Up)
換更大的機器:更多 CPU 核心、更多 RAM、更快的 SSD。
水平擴展(Scale Out)
加更多台機器,將資料和流量分散到多個節點。
實務建議:不要過早 Scale Out
水平擴展帶來的架構複雜度是真實的成本。在你的 DB 還沒真正到達瓶頸前, 優先考慮索引優化、Query 調整、加 Cache。Scale Out 是最後的手段,不是第一選擇。
讀寫分離(Read/Write Splitting)
在嘗試 Sharding 之前,讀寫分離通常是你能做的最划算的一步。 它的概念很簡單:把寫入集中到一台 Primary,把讀取分散到多台 Replica。
在 Node.js 生態中,最直接的實作方式是針對 Primary 和 Replica 建立不同的連線, 並在業務層依據操作類型選擇對應的 DB 實例:
Replication Lag 問題(必須了解)
Primary 和 Replica 之間的複製是「非同步」的,這意味著主節點寫入成功後, Replica 可能需要幾毫秒到幾百毫秒才能同步到最新資料。 這段時間差稱為 Replication Lag,是讀寫分離最容易踩到的坑。
Sharding — 水平分片
讀寫分離解決了 Read 的瓶頸,但無法解決 Write 瓶頸和 Storage 瓶頸。 當你的業務規模進一步增長,寫入量超過單台 Primary 的承載上限, 或者資料量超過單台磁碟的儲存上限,你就需要 Sharding。
Sharding 的核心思想是:把資料表拆分成多個「片段(Shard)」, 每個 Shard 都是一台獨立的資料庫,只存放整體資料的一個子集。
兩種 Sharding 策略
選擇 Sharding Key 和分片方式,是整個 Sharding 設計中最關鍵的決策。 不同的策略有不同的取捨,選錯了之後要改代價極高。
Hash Sharding(最均勻)
把 Sharding Key 進行 Hash,再取模決定落在哪個 Shard。 資料分布最均勻,不容易產生某個 Shard 特別忙的 Hot Spot 問題。
Range Sharding(有序查詢友好)
根據 Sharding Key 的「值域範圍」來分配 Shard。 適合有時間序列或有序 ID 的場景,範圍查詢效率極高。
Hash vs Range:如何選?
如果你的主要查詢是「根據 ID 查單筆資料」,選 Hash Sharding(均勻、防 Hot Spot)。 如果你的主要查詢是「查某個時間範圍或 ID 範圍的資料」,選 Range Sharding(範圍查詢快)。 實務上,許多大型系統(如 Google Bigtable、HBase)同時支援兩種策略,依表格特性選擇。
Sharding 的痛點
Sharding 不是免費的午餐。它帶來了一系列在單台 DB 時代從未需要面對的問題, 其中最痛的就是跨 Shard 的 JOIN 和分散式事務。
面對跨 Shard JOIN 問題,實務上有四種常見的處理方式, 各有取捨,選擇哪種取決於你的查詢模式和業務特性:
應用層 Join(Application-Side Join)
在程式碼中發兩次 Query,先查 users,再查 orders,最後在記憶體裡合併。
Denormalization(反正規化)
把需要 JOIN 的欄位直接存在相關聯的表裡,用「空間換複雜度」。
Global Tables(全域表)
把小型的、不常變動的參考資料表(如:商品分類、國家代碼)複製到所有 Shard, 讓每個 Shard 都能在本地完成 JOIN。
Co-location(相同 Shard Key)
設計時確保需要 JOIN 的兩張表使用相同的 Sharding Key, 這樣同一個 userId 的 users 和 orders 會落在同一個 Shard,本地 JOIN 即可。
選擇正確的時機
Sharding 是一個不可逆的架構決策。一旦引入,想要「去 Sharding」的成本極高。 因此,必須在真正需要的時候才引入,而不是「未來可能需要」就提前做。
什麼時候需要 Sharding
Write 達到瓶頸
單台 Primary 的寫入量持續超過 10K TPS,且讀寫分離和 Cache 已無法緩解。
資料量超過單台磁碟
單台資料量超過 10TB,備份時間過長,或磁碟費用超過合理預算。
有明確分片維度
業務上有天然的隔離維度(按地區、按租戶、按用戶 ID),且 JOIN 需求不多。
在 Sharding 之前,應該先嘗試(按順序)
加索引優化查詢
確保所有高頻查詢都有適當的複合索引(Composite Index),這是最便宜的優化。
讀寫分離
把 80% 的讀流量分散到 Replica,大幅降低 Primary 的壓力。
加 Cache(Redis)
把熱點資料放入 Redis,讓大多數讀取請求根本不需要打到 DB。
垂直擴展(換大機器)
升級 CPU、RAM、更快的 NVMe SSD,在不改架構的情況下獲得立即的效能提升。
最後才是 Sharding
上面四步都做了還不夠,才考慮 Sharding,並做好充分的架構規劃。
管理工具推薦
Vitess
MySQL ShardingYouTube 使用的 MySQL Sharding 解決方案,提供透明的 Sharding 路由, 應用層幾乎不需要改動。支援 Online Schema Changes。
Citus
PostgreSQL ShardingPostgreSQL 的 Sharding 擴充套件,支援分散式查詢、並行執行, 已被 Microsoft Azure 收購並整合到 Azure Database for PostgreSQL。
PlanetScale
Serverless MySQL基於 Vitess 的雲端 MySQL Sharding 服務,適合 Serverless 架構。 提供 Non-blocking Schema Changes 和 Database Branching(類似 Git)。
架構師的視角
大多數公司永遠不需要 Sharding。如果你的業務能讓一台 PostgreSQL 撐住,那就一台撐到底。 Sharding 是 Netflix、Uber、Twitter 這種規模的解法,不是所有系統都需要的。 過早引入 Sharding 的架構複雜度,會嚴重拖慢開發速度,得不償失。