攜程 Dynamo 風格存儲的落地實踐

作者簡介

根泰,攜程高級後端開發工程師,關注數據存儲和數據庫領域。

遐齡,攜程研發總監,關注大數據存儲、性能調優。

Dynamo 風格數據庫來源於亞馬遜的 Dynamo: Amazon’s Highly Available Key-value Store 論文,在該論文中論述了一種無主複製的數據庫,受此啓發,攜程酒店開發了多存儲介質預定庫 Hare 和高可用性高性能的動態信息存儲服務 InfoKeeper。本文將介紹 Dynamo 風格的無主複製數據庫,及其在攜程酒店的實踐。

一、Dynamo 風格數據庫

在分佈式系統中,爲了提高數據的可用性和性能,通常會將同樣的數據複製多份,分擔讀寫請求和主備切換,在複製形式上,主要有單主複製、多主複製、無主複製。

1.1 單主複製

圖片

在單主複製中,只有一個主節點可以寫入,數據從主節點複製到從節點,從節點可以承擔讀請求,單主複製的結構簡單,易於實現,沒有數據衝突。但是寫入依賴主節點,寫入性能由主節點的性能決定,主從節點之間存在複製延遲(在從節點上讀取到的數據不一定是最新的數據),在主節點發生故障進行主從切換的時候存在數據丟失或者寫入的不可用。

1.2 多主複製

圖片

在多主複製中,有多個主節點承擔寫入的請求,相比於單主複製,數據的寫入請求被多個主節點分擔,但主從節點之間的複製延遲問題依然存在。除此之外,兩個主節點對同一份數據的併發寫入需要衝突解決機制決定以哪次寫入爲準。

1.3 無主複製

Dynamo 風格的數據庫就是無主複製,寫入的請求不會經過特定的主節點複製到從節點,所有的節點都可以承擔讀取和寫入,容忍寫入時的不一致,在讀取時解決不一致。

假設一個數據庫中有三個節點,存儲的鍵值對 X=1。在下面的示意圖中,三個節點都收到了同一個寫入的請求,C 節點寫入失敗。

圖片

此時,三個節點內鍵值 X 對應的 value 是不一樣的,收到讀請求後自然會返回不同的值。

圖片

從上帝視角看,此時此刻,鍵值 X 對應的 value 應該是 100,但對於一個運行的系統,需要一個機制解決下面兩個問題,這個機制稱爲仲裁。

1.4 嚴格仲裁

使用時間戳或者版本號判定哪個值爲正確的值:時間戳最大的或者版本號最大的,代表數據是最新的,最新的數據就是正確的數據。

R+W>N,N:總的節點個數,W: 判斷寫入成功所需的節點個數,R:讀取時至少需要讀取成功的節點個數,W+R>N 時總會讀到最新的數據。如下圖所示,藍色的節點表示寫入成功的節點,即 W=3,當 R=3 時,讀取成功的節點和寫入成功的節點一定會有交集。W 越小,寫入的可用性更高,寫性能越好,R 越小,讀的可用性更高,讀性能越好。

圖片

假設單個節點的可用性 P=99.9%,以此來計算無主複製時的讀和寫的可用性,不同的 R、W 的可用性情況如下表所示,以 N=3 舉例,R=1 時讀的可用性等於圖片

zZ3EC0

根據表中所示,在 N=3,R=W=2 時,讀和寫的可用性都比單個節點的讀寫可用性高,這也是 Dynamo 風格數據庫使用的推薦配置。

1.5  寬鬆仲裁

在嚴格仲裁時,如果達不到嚴格仲裁的 R+W>N 時會返回調用端錯誤碼,假設 N=5,W=R=3,讀取的時候讀了 5 個節點,但是三個節點讀失敗了,只有兩個節點讀成功了,此時如果以兩個節點的結果比較版本號或者時間戳,得到的數據有可能是錯誤的,也有可能是正確的。

如果我們的系統能夠忍受返回不新鮮的數據的可能性,那麼使用寬鬆仲裁是提高系統可用性的一種辦法。我們來定義寬鬆仲裁:在系統達不到嚴格仲裁的條件時,利用僅有的條件返回調用端結果,注意,必須是先嚐試滿足嚴格仲裁,達不到嚴格仲裁時使用僅有的條件返回調用端結果,比如,N=5,R=W=3,在讀取數據時先讀取三個節點,兩個節點讀取失敗,爲了滿足嚴格仲裁,再讀取剩餘的兩個節點,但是一個成功,一個失敗,此時一共有兩個節點讀取成功,使用兩個節點的數據寬鬆仲裁,得出結果,而不是一開始就只讀兩個節點,這兩種方式讀取到錯誤數據的概率差別很大。

使用寬鬆仲裁時得到正確數據的概率如下表所示,假設單個節點的可用性 P=99.9%,N=1,R=W=1 時,讀和寫的可用性是圖片,N=3,R=1,W=1 時讀到錯誤數據的概率圖片

rAoRTn

無主複製的數據庫在寫入的時候容忍了部分節點的不一致,但是我們希望每個節點上的數據儘可能的完整,這就需要節點版本補齊。

1.6 節點間的版本補齊

1)寫修復,節點寫失敗在寫入的時候已經是被感知到的,可以通過消息隊列等方式異步的在寫入失敗之後補償修復。

圖片

2)讀修復,在讀取數據的時候,已經知道了節點間的數據不一致,此時可以根據仲裁得出的數據來修復版本滯後的節點上的數據。

圖片

3)巡檢,主動的掃描介質間的數據,根據仲裁的結果修復數據。

圖片

二、由無主複製向多介質存儲擴展

前面介紹無主複製數據庫的時候一直在使用 “節點” 這個概念,這裏對節點做一個定義:運行同一套代碼的、擁有完全相同功能的進程,比如 Redis 的 master 和 slave 節點。

在攜程酒店的預定訂單和價態信息存儲中,選擇合適的存儲介質一直是一個核心的技術問題,我們希望數據不僅在介質內有互備(Redis 的 master 和 slave),還能有介質間的互備(比如 Redis 和 Trocks),因爲同一個存儲介質總是擁有相似的運作機制,同時出問題的概率更高。

在多介質數據存儲中,我們對前面理論部分用存儲介質代替 “節點” 後的語義就是:數據同時寫到多個存儲介質中,容忍部分存儲介質的寫入失敗,在讀出數據時,仲裁決定整個系統中數據最終的值,整個系統能夠容忍單一存儲介質級別不可用的情況,系統的穩定性從容忍單個節點故障提升到了存儲介質級別。

三、Hare:多存儲介質的預定庫

Hare 的名稱來源於成語 “狡兔三窟”,數據存儲在多個介質中,以保證數據的安全。Hare 承擔攜程酒店預定庫的功能,主要用於存儲在用戶下單的各個環節(創單、支付、提交)中產生的訂單相關數據。在訂單完成提交後從 Hare 同步到訂單庫,進入訂單處理環節。Hare 的架構圖如下圖所示,應用層代碼管理底層的 Redis、Trocks、Hbase 的寫入和讀取,以及仲裁返回給調用端的數據。介質間版本補齊使用寫修復。

圖片

Hare 內部採用寬鬆仲裁,N=3,W=1,R=1,使用版本號判斷最新版本。需要特別指出的是,W=1 並非任何一個介質寫入成功就算成功,Hare 內部 “期望” 的寫入成功個數爲 2,但是當所有介質寫入完成後,寫入成功的介質個數依然沒有達到 2,就會優先考慮可用性,寫入成功的個數等於 1 也算寫入成功。

當 W=1 時,嚴格仲裁的 R 應該等於 3,Hare 內部會讀所有的 3 個介質並比較版本號,返回版本號最大的數據。但如果讀完所有數據,依然只有一個介質讀成功,還是會以成功的這個介質的數據返回給調用方。所以寬鬆仲裁的含義是,在使用嚴格仲裁但達不到嚴格仲裁的條件時,優先保證可用性。寫入和讀取時的流程圖如下所示。

圖片

四、InfoKeeper:高可用高性能的動態信息存儲

InfoKeeper 是對 Hare 架構在酒店價態量存儲場景下的改進,Hare 作爲下單場景用,對性能要求較低,但對數據的可靠性要求更高。但在酒店的價態量存儲中,對性能要求更高,數據可靠性要求較下單場景低,所以 InfoKeeper 中存儲介質的個數較 Hare 更少,選擇了 Redis 和 Trocks 兩個存儲介質,仲裁的 N=2,W=1,R=1。

我們將 InfoKeeper 中參與仲裁的介質稱爲主介質(圖中綠色),將只會寫入但是不參與仲裁的介質稱爲從介質(圖中淡藍色),從介質的寫入是否成功都不會影響對客戶端的響應。介質間的版本補齊使用寫修復。在酒店價態量存儲中架構圖如下。

圖片

InfoKeeper 寫入的流程圖如下。

圖片

InfoKeeper 現在支持的存儲介質有 redis、trocks、mysql、es、hbase、oceanbase、Tikv、qmq、kafka、soa。qmq 通常作爲推送增量的方式,kafka 用於推送離線數據,soa 用於通過 soa 接口調用的方式更新服務端的緩存。因爲接口較消息隊列延時更低,所以 soa 面向對緩存新鮮程度要求很高的使用方,比如酒店查詢服務,在 InfoKeeper 中將消息隊列和 soa 接口當作一種存儲介質看待,只是這種存儲介質不能提供讀功能。

InfoKeeper 中存儲的數據目前在百億級別,InfoKeeper 完成了這些數據的存儲、承擔了 40 萬 QPS 的讀能力,以及數據從存儲方到各個使用方的高效流轉。得益於強大的讀能力(強大的讀寫能力主要是因爲選擇了性能更好的 KV 型存儲介質爲主介質,可以根據數據讀取方對性能和數據新鮮度的要求,選擇對應的存儲介質和仲裁的方式),一些散落在各個使用方的緩存廢棄,改爲直接從 InfoKeeper 讀。根據統計,InfoKeeper 節省了 20% 的硬件成本,數據的流轉效率較以往使用關係型數據庫存儲,使用方從關係型數據庫拉取的方式大大提高,還消除了關係型數據庫的單點性能限制。

圖片

建立緩存的一種新模式

在 InfoKeeper 前面的架構圖中,如果將主介質改爲關係型數據庫,從介質改爲 redis,就實現了爲 DB 建緩存的目的,只是把從 DB 拉數據改爲了主動往 redis 寫數據,減輕了 DB 的壓力。如果需要建多份緩存,只需要多掛幾個從介質就可以實現。目前酒店的房型通用緩存就是使用這種方式。

五、設計目標的驗證

怎麼確認多介質存儲系統符合設計預期,能夠容忍存儲介質級別的故障?Hare 上線 6 個季度,InfoKeeper 上線 4 個季度以來,我們在每個季度都會對 Hare 和 InfoKeeper 做單個介質注入故障的演練,在演練期間應用和上下游正常,在注入故障恢復之後,寫修復最終追趕成功,可以確認系統符合設計預期。

六、展望

現在 InfoKeeper 和 Hare 還在應用代碼層面,沒有形成通用的組件,新的業務的加入需要在現有代碼的基礎上增加業務邏輯,開發者對底層的多介質存儲的代碼是有感的,也可能需要修改多介質存儲層的代碼以更好的貼合新的業務。

我們計劃對 Infokeeper 和 Hare 的代碼進行合併,形成一個通用的組件,讓新的使用方能對多介質存儲層無感,做到開箱即用,降低多介質存儲的使用門檻,使得使用方能更專注於業務代碼。

“攜程技術” 公衆號

分享,交流,成長

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/mxZLbndr1ROMTqDJ3QQ45A