如何利用MongoDB實現高性能,高可用的雙活應用架構?

投資界有一句至理名言——“不要把雞蛋放在同一個籃子裡”。說的是投資需要分解風險,以免孤注一擲失敗之後造成巨大的損失。

如何利用MongoDB實現高性能,高可用的雙活應用架構?

隨著企業服務窗口的不斷增加,業務中斷對很多企業意味著毀滅性的災難,因此,跨多個數據中心的應用部署成為了當下最熱門的話題之一。

如今,在跨多個數據中心的應用部署最佳實踐中,數據庫通常負責處理多個地理區域的讀取和寫入,對數據變更的複製,並提供儘可能高的可用性、一致性和持久性。

但是,並非所有的技術在選擇上都是平等的。例如,一種數據庫技術可以提供更高的可用性保證,卻同時只能提供比另一種技術更低的數據一致性和持久性保證。

本文先分析了在現代多數據中心中應用對於數據庫架構的需求。隨後探討了數據庫架構的種類及優缺點,最後專門研究 MongoDB 如何適用於這些類別,並最終實現雙活的應用架構。

雙活的需求

當組織考慮在多個跨數據中心(或區域雲)部署應用時,他們通常會希望使用“雙活”的架構,即所有數據中心的應用服務器同時處理所有的請求。

如何利用MongoDB實現高性能,高可用的雙活應用架構?

圖 1:“雙活”應用架構

如圖 1 所示,該架構可以實現如下目標:

  • 通過提供本地處理(延遲會比較低),為來自全球的請求提供服務。

  • 即使出現整個區域性的宕機,也能始終保持高可用性。

  • 通過對多個數據中心裡服務器資源的並行使用,來處理各類應用請求,並達到最佳的平臺資源利用率。

“雙活”架構的替代方案是由一個主數據中心(區域)和多個災備(DR)區域(如圖 2 所示)所組成的主-DR(也稱為主-被)架構。

如何利用MongoDB實現高性能,高可用的雙活應用架構?

圖 2:主-DR 架構

在正常運行條件下,主數據中心處理請求,而 DR 站點處於空閒狀態。如果主數據中心發生故障,DR 站點立即開始處理請求(同時變為活動狀態)。

一般情況下,數據會從主數據中心複製到 DR 站點,以便在主數據中心出現故障時,能夠迅速實施接管。

如今,對於雙活架構的定義尚未得到業界的普遍認同,上述主-DR 的應用架構有時也被算作“雙活”。

區別在於從主站到 DR 站點的故障轉移速度是否夠快(通常為幾秒),並且是否能夠自動化(無需人為干預)。在這樣的解釋中,雙活體系架構意味著應用停機時間接近於零。

有一種常見的誤解,認為雙活的應用架構需要有多主數據庫。這樣理解是錯誤的,因為它曲解了多個主數據庫對於數據一致性和持久性的把握。

一致性確保了能讀取到先前寫入的結果,而數據持久性則確保了提交的寫入數據能夠被永久保存,不會產生衝突寫入;或是由於節點故障所產生的數據丟失。

雙活應用的數據庫需求

在設計雙活的應用架構時,數據庫層必須滿足如下四個方面的架構需求(當然,也要具備標準數據庫的功能,如具有:豐富的二級索引能力的查詢語言,低延遲地訪問數據,本地驅動程序,全面的操作工具等):

  • 性能,低延遲讀取和寫入操作。這意味著:能在本地數據中心應用的節點上,處理讀取和寫入操作。

  • 數據持久性,通過向多個節點的複製寫入來實現,以便在發生系統故障時,數據能保持不變。

  • 一致性,確保能讀取之前寫入的結果,而且在不同地區和不同節點所讀到的結果應該相同。

  • 可用性,當某個節點、數據中心或網絡連接中斷時,數據庫必須能繼續運行。另外,從此類故障中恢復的時間應儘可能短,一般要求是幾秒鐘。

分佈式數據庫架構

針對雙活的應用架構,一般有三種類型的數據庫結構:

  • 使用兩步式提交的分佈式事務。

  • 多主數據庫模式,有時也被稱為“無主庫模式”。

  • 分割(分片)數據庫具有多個主分片,每個主分片負責數據的某個唯一片區。

下面讓我們來看看每一種結構的優缺點。

兩步式提交的分佈式事務

分佈式事務方法是在單次事務中更新所有包含某個記錄的節點,而不是寫完一個節點後,再(異步)複製到其他節點。

該事務保證了所有節點都會接收到更新,否則如果某個事務失敗,則所有節點都恢復到之前的狀態。

雖然兩步式提交協議可以確保持久性和多節點的一致性,但是它犧牲了性能。

兩步式提交協議要求在事務中所有參與的節點之間都要進行兩步式的通信。即在操作的每個階段,都要發送請求和確認,以確保每個節點同時完成了相同的寫入。

當數據庫節點分佈在多個數據中心時,會將查詢的延遲從毫秒級別延長到數秒級別。

而在大多數應用,尤其是那些客戶端是用戶設備(移動設備、Web 瀏覽器、客戶端應用等)的應用中,這種響應級別是不可接受的。

多主數據庫

多主數據庫是一種分佈式的數據庫,它允許某條記錄只在多個群集節點中一個之上被更新。而寫操作通常會複製該記錄到多個數據中心的多個節點上。

從表面上看,多主數據庫應該是實現雙活架構的理想方案。它使得每個應用服務器都能不受限地讀取和寫入本地數據的副本。但是,它在數據一致性上卻有著嚴重的侷限性。

由於同一記錄的兩個(或更多)副本可能在不同地點被不同的會話同時更新。這就會導致相同的記錄會出現兩個不同的版本,因此數據庫(有時是應用本身)必須通過解決衝突來解決不一致的問題。

常用的衝突解決策略是:最近的更新“獲勝”,或是具有更多修改次數的記錄“獲勝”。因為如果使用其他更為複雜的解決策略,則性能上將受到顯著的影響。

這也意味著,從進行寫入到完成衝突解決機制的這個時間段內,不同的數據中心會讀取到某個相同記錄的不同值和衝突值。

分區(分片)數據庫

分區數據庫將數據庫分成不同的分區,或稱為分片。每個分片由一組服務器來實現,而每個服務器都包含一份分區數據的完整副本。這裡關鍵在於每個分片都保持著對數據分區的獨有控制權。

對於任何給定時間內的每個分片來說,由一臺服務器充當主服務器,而其他服務器則充當其副本。數據的讀取和寫入被髮布到主數據庫上。

如果主服務器出於任何原因的(例如硬件或網絡故障)失敗,則某一臺備用服務器會自動接任為主服務器的角色。

數據庫中的每條記錄都屬於某個特定的分區,並由一個分片來進行管理,以確保它只會被主分片進行寫入。分片內的記錄映射到每個分片的一個主分片,以確保一致性。

由於集群內包含多個分片,因此會有多個主分片(多個主分區),因此這些主分片可以被分配到不同的數據中心,以確保都在每個數據中心的本地都能發生寫入操作,如圖 3 所示:

如何利用MongoDB實現高性能,高可用的雙活應用架構?

圖 3:分區數據庫

分片數據庫可用於實現雙活的應用架構,其方法是:至少部署與數據中心一樣多的分片,併為分片分配主分片,以便每個數據中心至少有一個主分片,如圖 4 所示:

如何利用MongoDB實現高性能,高可用的雙活應用架構?

圖 4:具有分片數據庫的雙活架構

另外,通過配置分片能保證每個分片在各種數據中心裡至少有一個副本(數據的副本)。

例如,圖 4 中的圖表描繪了跨三個數據中心的分佈式數據庫架構:

  • 紐約(NYC)

  • 倫敦(LON)

  • 悉尼(SYD)

群集有三個分片,每個分片有三個副本:

  • NYC 分片在紐約有一個主分片,在倫敦和悉尼有副本。

  • LON 分片在倫敦有一個主分片,在紐約和悉尼有副本。

  • SYD 分片在悉尼有一個主分片,在倫敦和紐約有副本。

通過這種方式,每個數據中心都有來自所有分片的副本,因此本地應用服務器可以讀取整個數據集和一個分片的主分片,以便在其本地進行寫入操作。

分片數據庫能滿足大多數使用場景的一致性和性能要求。由於讀取和寫入發生在本地服務器上,因此性能會非常好。

從主分片中讀取時,由於每條記錄只能分配給一個主分片,因此保證了一致性。

例如:我們在美國的新澤西州和俄勒岡州有兩個數據中心,那麼我們可以根據地理區域(東部和西部)來分割數據集,並將東海岸用戶的流量路由到新澤西州的數據中心。

因為該數據中心包含的是主要用於東部的分片;並將西海岸用戶的流量路由到俄勒岡州數據中心,因為該數據中心包含的是主要用於西部的分片。

我們可以看到分片的數據庫為我們提供了多個主數據庫的所有好處,而且避免了數據不一致所導致的複雜性。

應用服務器可以從本地主服務器上進行讀取和寫入,由於每個主服務器擁有各自的記錄,因此不會出現任何的不一致。相反,多主數據庫的解決方案則可能會造成數據丟失和讀取的不一致。

數據庫架構比較

如何利用MongoDB實現高性能,高可用的雙活應用架構?

圖 5:數據庫架構比較

圖 5 提供了每一種數據庫架構在滿足雙活應用需求時所存在的優缺點。在選擇多主數據庫和分區數據庫時,其決定因素在於應用是否可以容忍可能出現的讀取不一致和數據的丟失問題。

如果答案是肯定的,那麼多主數據庫可能會稍微容易部署些。而如果答案是否定的,那麼分片數據庫則是最好的選擇。

由於不一致性和數據丟失對於大多數應用來說都是不可接受的,因此分片數據庫通常是最佳的選擇。

MongoDB 雙活應用

MongoDB 是一個分片數據庫架構的範例。在 MongoDB 中,主服務器和次服務器集的構造被稱為副本集。副本集為每個分片提供了高可用性。

一種被稱為區域分片(Zone Sharding)的機制被配置為:由每個分片去管理的數據集。如前面所提到的,ZoneSharding 可以實現地域分區。

白皮書《MongoDB多數據中心部署》:

  • https://www.mongodb.com/collateral/mongodb-multi-data-center-deployments?utm_medium=dzone-synd&utm_source=dzone&utm_content=active-application&jmp=dzone-ref

Zone Sharding 相關文檔:

  • https://docs.mongodb.com/manual/tutorial/sharding-segmenting-data-by-location/的“分區(分片)數據庫”部分描述了 MongoDB 具體實現和運作的細節。

其實許多組織,包括:Ebay、YouGov、Ogilvyand Maher 都正在使用 MongoDB 來實現雙活的應用架構。

除了標準的分片數據庫功能之外,MongoDB 還提供對寫入耐久性和讀取一致性的細粒度控制,並使其成為多數據中心部署的理想選擇。對於寫入,我們可以指定寫入關注(write concern)來控制寫入的持久性。

Writeconcern 使得應用在 MongoDB 確認寫入之前,就能指定寫入的副本數量,從而在一個或多個遠程數據中心內的服務器上完成寫入操作。籍此,它保證了在節點或數據中心發生故障時,數據庫的變更不會被丟失。

另外,MongoDB 也補足了分片數據庫的一個潛在缺點:寫入可用性無法達到 100%。

由於每條記錄只有一個主節點,因此如果該主節點發生故障,則會有一段時間不能對該分區進行寫入。

MongoDB 通過多次嘗試寫入,大幅縮短了故障切換的時間。通過多次嘗試的寫入操作,MongoDB 能夠自動應對由於網絡故障等暫時性系統錯誤而導致的寫入失敗,因此也大幅簡化了應用的代碼量。

MongoDB 的另一個適合於多數據中心部署的顯著特徵是:MongoDB 自動故障切換的速度。

當節點或數據中心出現故障或發生網絡中斷時,MongoDB 能夠在 2-5 秒內(當然也取決於對它的配置和網絡本身的可靠性)進行故障切換。

發生故障後,剩餘的副本集將根據配置去選擇一個新的主切片和 MongoDB 驅動程序,從而自動識別出新的主切片。一旦故障切換完成,其恢復進程將自動履行後續的寫入操作。

對於讀取,MongoDB 提供了兩種功能來指定所需的一致性級別。

首先,從次數據進行讀取時,應用可以指定最大時效值(maxStalenessSeconds)。

這可以確保次節點從主節點複製的滯後時間不能超過指定的時效值,從而次節點所返回的數據具有其時效性。

另外,讀取也可以與讀取關注(ReadConcern)相關聯,來控制查詢到的返回數據的一致性。

例如,ReadConcern 能通過一些返回值來告知 MongoDB,那些被複制到副本集中的多數節點上的數據。

這樣可以確保查詢只讀取那些沒有因為節點或數據中心故障而丟失的數據,並且還能為應用提供一段時間內數據的一致性視圖。

MongoDB 3.6 還引入了“因果一致性(causal consistency)”的概念,以保證客戶端會話中的每個讀取操作,都始終只“關注”之前的寫入操作是否已完成,而不管具體是哪個副本正在為請求提供服務。

通過在會話中對操作進行嚴格的因果排序,這種因果一致性可以確保每次讀取都始終遵循邏輯上的一致,從而實現分佈式系統的單調式讀取(monotonic read)。而這正是各種多節點數據庫所無法滿足的。

如果想學習Java性能優化,工程化、高性能及分佈式、深入淺出。微服務、Spring,MyBatis,Netty源碼分析的朋友可以加下454377428群裡有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給大家。

因果一致性不但使開發人員,能夠保留過去傳統的單節點式關係型數據庫,在實施過程中具備的數據嚴格一致性的優勢;又能將時下流行架構充分利用到可擴展和具有高可用性的分佈式數據平臺之上。

如何利用MongoDB實現高性能,高可用的雙活應用架構?

陳峻(Julian Chen) ,有著十多年的 IT 項目、企業運維和風險管控的從業經驗,日常工作深入系統安全各個環節。作為 CISSP 證書持有者,他在各專業雜誌上發表了《IT運維的“六脈神劍”》、《律師事務所IT服務管理》 和《股票交易網絡系統中的安全設計》等論文。他還持續分享並更新《廉環話》系列博文和各種外文技術翻譯,曾被(ISC)2 評為第九屆亞太區信息安全領袖成就表彰計劃的“信息安全踐行者”和 Future-S 中國 IT 治理和管理的 2015 年度踐行人物。


分享到:


相關文章: