Uber是如何花費巨大精力實現快取精確失效?

banq發表於2024-02-24


這篇文章介紹了Uber內部分散式資料庫Docstore的架構、挑戰以及他們構建的整合快取解決方案CacheFront。文章詳細介紹了CacheFront的設計、特性和實現,以及對最終結果的評估。透過CacheFront,Uber成功解決了在Docstore上擴充套件讀取工作負載的挑戰,並取得了顯著的效能提升和資源節約。

要點:
1、Uber 的 Docstore 是一個內部分散式資料庫,儲存數十 PB 的資料並處理數千萬個請求,是 Uber 最大的資料庫引擎之一。

2、為了應對大規模低延遲讀取需求,Uber 開發了整合的快取解決方案 CacheFront for Docstore,以降低資料庫引擎層的資源分配,並改善 P50 和 P99 延遲。

3、CacheFront 透過快取預留策略、利用變更資料捕獲和流服務 Flux、比較快取等功能,提供了強一致性的快取解決方案,併成功降低了資料庫引擎的負載,實現了低延遲讀取請求的需求。

快取讀取
CacheFront 使用快取預留策略來實現快取讀取:

  1. 查詢引擎層收到多一行的讀取請求
  2. 如果啟用了快取,請嘗試從 Redis 獲取行;將響應流式傳輸給使用者
  3. 從儲存引擎檢索剩餘行(如果有)
  4. 使用剩餘行非同步填充 Redis
  5. 將剩餘行流式傳輸給使用者

快取失效
“電腦科學中只有兩件難事:快取失效和命名。” 
——菲爾·卡爾頓

如果沒有任何顯式快取失效,快取條目將在配置的 TTL(預設情況下為 5 分鐘)內過期。雖然這在某些情況下可能沒問題,但大多數使用者希望更改的反映速度比 TTL 更快。預設的 TTL 可以降低,但這會降低我們的快取命中率,而不會顯著提高一致性保證。

1、有條件更新
Docstore 支援條件更新,可以根據過濾條件更新一行或多行。
例如,更新指定地區所有連鎖餐廳的假期安排。由於給定過濾器的結果可能會更改,因此我們的快取層無法確定哪些行將受到條件更新的影響,直到資料庫引擎中更新了實際行。

2、利用變更資料捕獲CDC 來使快取失效 
為了解決這個問題,我們利用了 Docstore 的變更資料捕獲和流服務 Flux。Flux 跟蹤儲存引擎層中每個叢集的MySQL binlog事件,並將事件釋出到消費者列表。Flux 為 Docstore CDC(更改資料捕獲)、複製、物化檢視、資料湖攝取以及驗證叢集中節點之間的資料一致性提供支援。 

3、在查詢引擎和 Flux 之間刪除重複的快取寫入
上述快取失效策略有一個缺陷。由於寫入在讀取路徑和寫入路徑之間同時發生在快取中,因此我們可能會無意中將過時的行寫入快取,從而覆蓋從資料庫檢索到的最新值。

為了解決這個問題,我們根據 MySQL 中行集的時間戳來刪除重複寫入,這實際上作為其版本。時間戳是從 Redis 中的編碼行值中解析出來的。

4、更強的點寫入一致性保證
雖然 Flux 允許我們比僅僅依賴 Redis TTL 來使快取條目失效更快,但它仍然為我們提供了最終一致性語義。然而,某些用例需要更強的一致性,例如讀自己寫,因此對於這些場景,我們向查詢引擎新增了專用 API,允許使用者在相應的寫入完成後顯式使快取的行無效。

更多細節點選標題


 

相關文章