在這篇論文之前 — 它所降落的世界
到 2000 年代末,雲端服務底下那層可擴充的儲存幾乎清一色是 NoSQL:Bigtable、Dynamo、PNUTS、Cassandra、MongoDB、CouchDB。這些系統為了撐到數十億使用者而放棄交易語意,改為提供最終一致性,等於把「並行操作的更新如何交錯」這件事整包丟回給每一位應用開發者去推理。另一條路的真資料庫則把儲存引擎、資料模型與查詢語言綁成一包,想要交易的團隊只能三個全收或全不收;而 Percolator、Tephra、Omid 這類在 key-value store 之上疊一層交易 API 的作法,也只做到快照隔離。真正提供分散式交易的系統,例如用 TrueTime 的 Spanner、用 hybrid-logical clock 的 CockroachDB,以及 Deuteronomy 這類已走拆解路線的設計,全都是在取得所有鎖的那一刻決定序列順序,而且要用 2f+1 個複本換容錯。同時,整個業界測試分散式系統靠的仍是非確定性的故障注入,而那正是最沒辦法重現自己剛剛抓到的那個 bug 的技術;模型檢查雖然能驗證協定,卻驗證不到真正出貨的實作。
術語 — 依本篇論文的用法
- Unbundled architecture(拆解式架構)
- 承 Lomet 等人的說法,指把交易元件(TC)與資料元件(DC)拆成不同系統的資料庫。FDB 拆得更徹底:交易系統、日誌系統與儲存系統是三個能各自擴充的層級,連交易日誌都從交易元件中解耦出來。
- Layer
- 疊在 FDB 之上的無狀態應用,負責提供 FDB 刻意不做的資料模型、查詢能力或其他資料庫功能。layer 直接繼承 FDB 的嚴格可序列化交易,這也是關聯式儲存、文件儲存與圖資料庫能共用同一個 key-value 核心的原因。
- Sequencer
- 負責替每一筆交易指派讀取版本與 commit version 的單例行程,版本以每秒一百萬個的速率推進。它發出的 commit version 定義了資料庫的序列歷史,並被直接當作 Log Sequence Number 使用。
- Resolver
- 執行 FDB 樂觀並行控制的無狀態行程,做法是拿交易的讀取範圍去比對一份近期被修改 key 範圍的歷史。key 空間被切分給多個 Resolver,交易必須被每一個 Resolver 都放行才能提交。
- LogServer
- 日誌系統的成員,扮演複寫、分片、分散式的持久化佇列,替特定的 StorageServers 保存 write-ahead log 資料。必須全部 k = f+1 台指定的 LogServer 都回報紀錄已持久化,提交才會被確認。
- Storage team
- 非同步複寫同一個 shard 的那 k = f+1 台 StorageServer。一台 StorageServer 會隸屬許多 team,讓它的資料廣泛散開;一旦某個 team 失去成員,DataDistributor 就會把 shard 搬走。
- Known Committed Version(KCV)
- 某個 Proxy 已確認提交的最大 LSN,也就是所有複本 LogServer 都已回報持久化的那個位置。Proxies 會把自己的 KCV 夾帶在日誌訊息中,而復原時會取所有 LogServer 上 KCV 的最大值作為前一個 epoch 的結束版本。
- Recovery Version(RV)
- 為失效的 epoch 選定的 redo 日誌結尾,計算方式是取舊 LogServers 上 Durable Version 的最小值。舊 LogServers 與 StorageServers 中版本高於 RV 的資料一律丟棄,這就是 FDB 用來取代 undo 日誌處理的全部機制。
- Buggification
- 一種故障注入手法:程式碼本身在許多位置留下讓模擬器注入不尋常、但不違反合約之行為的機會,例如讓通常成功的操作回傳錯誤、替快速操作插入延遲,或挑一個奇怪的調校參數值。它讓罕見狀態變得常見,也確保沒有任何調校值會悄悄成為正確性的必要條件。