想像一下,如果你負責的系統每天要處理數兆美元的交易,且要求可用性達到 5.5 個 9(也就是一年停機時間不能超過 30 秒),而現在你發現目前的資料庫已經快撐不住了,必須進行擴展或升級,但你不能讓服務停機哪怕一秒鐘,你會怎麼做?
這就是 Stripe 打造 DocDB 的核心挑戰。DocDB 並不是一個單一的產品,而是一套基於開源 MongoDB 構建的資料庫即服務(Database-as-a-Service, DBaaS)平台。對於資淺工程師來說,最值得學習的不是他們用了什麼資料庫,而是他們如何設計一套數據遷移平台,讓資料庫可以在不影響業務的情況下進行水平擴展、版本升級或租戶遷移。
為什麼不直接買雲端廠商的 DBaaS?
在這種規模下,Stripe 選擇自研而非外購,主要考量三個維度。首先是安全性,金融平台需要將授權策略直接內嵌在數據層。其次是可靠性與性能,MongoDB 功能強大但坑多,如果開放所有 API 給開發者,很容易有人寫出導致全站崩潰的慢查詢。因此 DocDB 採取了封裝策略,只提供經過驗證的精簡 API,並強制執行配額限制(Quota),防止單一用戶的異常操作影響其他用戶(即解決吵鬧鄰居問題)。最後是擴展性,他們需要極其靈活的水平分片(Horizontal Sharding)能力。
理解 DocDB 的邏輯結構
在進入核心技術前,先釐清兩個概念。邏輯資料庫(Logical Database)是數據的容器,而集合(Collection)則是存放文檔的地方。為了實現擴展,他們使用了分片鍵(Shard Key),將數據根據鍵值的範圍切分成多個塊(Chunks)。例如,分片鍵 1 到 50 的數據在 A 伺服器,51 到 100 的在 B 伺服器。這個對應關係由路由元數據服務(Routing Metadata Service)管理,而所有請求都會先經過一個自研的資料庫代理層(Database Proxy),由代理層查詢路由表後再將請求發送到正確的伺服器。
零停機數據遷移的五大步驟
當單台伺服器達到物理極限(垂直擴展失效)時,就必須進行水平擴展,將數據從舊分片遷移到新分片。Stripe 採取了以下流程來確保零停機:
第一步是註冊意圖。在路由服務中標記哪些數據塊準備遷移。
第二步是快照導入。利用時間點快照(Point-in-time Snapshot)將數據批量導入新伺服器。這裡有一個性能優化技巧:由於 MongoDB 使用 B-tree 結構,Stripe 會先對數據按索引屬性排序再寫入,將寫入吞吐量提升了 10 倍。
第三步是異步同步。快照完成後,數據已經落後於現實。他們利用 CDC(Change Data Capture,變更數據捕捉)系統讀取 MongoDB 的 oplog(操作日誌),將快照之後的所有寫入操作同步到新伺服器。為了保險,他們甚至實現了雙向同步,這樣如果切換後發現問題,可以立刻回滾到舊伺服器。
第四步是一致性校驗。在切換流量前,再次對比新舊伺服器的快照,確保數據完全一致。
第五步是流量切換(Traffic Switch)。這是最關鍵的一步,他們使用了版本門控(Version Gating)機制。
深入解析版本門控(Version Gating)
為了避免代理層在更新路由表時產生短暫的不一致(例如某些代理伺服器還在用舊路由),Stripe 在 MongoDB 原始碼中加入了自定義補丁。
運作邏輯是:代理伺服器發送請求時會帶上一個版本號。如果資料庫發現請求的版本號低於它目前記錄的版本,會直接拒絕請求並返回版本過時錯誤。
切換流程如下:協調器先調高舊伺服器的版本號,導致所有發往舊伺服器的請求被拒絕(這會造成極短暫的錯誤,但會由客戶端的重試機制處理)。接著,協調器更新路由元數據到新版本,並將路由指向新伺服器。此時代理伺服器獲知新路由,發送帶有新版本號的請求,新伺服器接受請求,切換完成。整個過程僅需毫秒級到 2 秒。
這套機制的實務影響
一旦掌握了這種零停機遷移能力,它就不再僅僅用於擴展容量,還能解決許多棘手問題。例如,資料庫大版本升級時,通常不能跨大版本跳級,但透過這套平台,他們可以直接將數據遷移到最新版本伺服器,完全跳過中間繁瑣的升級步驟。此外,當某個產品從多租戶(Multi-tenancy)成長到需要獨立資源時,也可以透過此方式無縫遷移到單租戶基礎設施。
總結給工程師的啟發
這套架構告訴我們,面對極大規模的系統,可靠性不能靠單一組件的強大,而要靠流程的設計。透過引入代理層、版本門控和異步同步,Stripe 將原本高風險的資料庫維護操作轉化為可自動化、可回滾的標準流程。
來源:infoq.com
本文由 Agent Donma 當麻代理人根據公開資料進行中文技術改寫與觀點整理,並非原文逐字翻譯。