您好,登錄后才能下訂單哦!
這篇文章主要講解了“保證服務器分布式系統(tǒng)數(shù)據(jù)一致性的方法有哪些”,文中的講解內(nèi)容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“保證服務器分布式系統(tǒng)數(shù)據(jù)一致性的方法有哪些”吧!
問題的起源
在電商等業(yè)務中,系統(tǒng)一般由多個獨立的服務組成,如何解決分布式調(diào)用時候數(shù)據(jù)的一致性?
具體業(yè)務場景如下,比如一個業(yè)務操作,如果同時調(diào)用服務 A、B、C,需要滿足要么同時成功;要么同時失敗。A、B、C 可能是多個不同部門開發(fā)、部署在不同服務器上的遠程服務。
在分布式系統(tǒng)來說,如果不想犧牲一致性,CAP 理論告訴我們只能放棄可用性,這顯然不能接受。為了便于討論問題,先簡單介紹下數(shù)據(jù)一致性的基礎(chǔ)理論。
強一致
當更新操作完成之后,任何多個后續(xù)進程或者線程的訪問都會返回最新的更新過的值。這種是對用戶最友好的,就是用戶上一次寫什么,下一次就保證能讀到什么。根據(jù) CAP 理論,這種實現(xiàn)需要犧牲可用性。
弱一致性
系統(tǒng)并不保證續(xù)進程或者線程的訪問都會返回最新的更新過的值。系統(tǒng)在數(shù)據(jù)寫入成功之后,不承諾立即可以讀到最新寫入的值,也不會具體的承諾多久之后可以讀到。
最終一致性
弱一致性的特定形式。系統(tǒng)保證在沒有后續(xù)更新的前提下,系統(tǒng)最終返回上一次更新操作的值。在沒有故障發(fā)生的前提下,不一致窗口的時間主要受通信延遲,系統(tǒng)負載和復制副本的個數(shù)影響。DNS 是一個典型的最終一致性系統(tǒng)。
在工程實踐上,為了保障系統(tǒng)的可用性,互聯(lián)網(wǎng)系統(tǒng)大多將強一致性需求轉(zhuǎn)換成最終一致性的需求,并通過系統(tǒng)執(zhí)行冪等性的保證,保證數(shù)據(jù)的最終一致性。但在電商等場景中,對于數(shù)據(jù)一致性的解決方法和常見的互聯(lián)網(wǎng)系統(tǒng)(如 MySQL 主從同步)又有一定區(qū)別,群友的討論分成以下 6 種解決方案。
1. 規(guī)避分布式事務——業(yè)務整合
業(yè)務整合方案主要采用將接口整合到本地執(zhí)行的方法。拿問題場景來說,則可以將服務 A、B、C 整合為一個服務 D 給業(yè)務,這個服務 D 再通過轉(zhuǎn)換為本地事務的方式,比如服務 D 包含本地服務和服務 E,而服務 E 是本地服務 A ~ C 的整合。
優(yōu)點:解決(規(guī)避)了分布式事務。
缺點:顯而易見,把本來規(guī)劃拆分好的業(yè)務,又耦合到了一起,業(yè)務職責不清晰,不利于維護。
由于這個方法存在明顯缺點,通常不建議使用。
2. 經(jīng)典方案 - eBay 模式
此方案的核心是將需要分布式處理的任務通過消息日志的方式來異步執(zhí)行。消息日志可以存儲到本地文本、數(shù)據(jù)庫或消息隊列,再通過業(yè)務規(guī)則自動或人工發(fā)起重試。人工重試更多的是應用于支付場景,通過對賬系統(tǒng)對事后問題的處理。
消息日志方案的核心是保證服務接口的冪等性。
考慮到網(wǎng)絡通訊失敗、數(shù)據(jù)丟包等原因,如果接口不能保證冪等性,數(shù)據(jù)的唯一性將很難保證。
eBay 方式的主要思路如下。
Base:一種 Acid 的替代方案
此方案是 eBay 的架構(gòu)師 Dan Pritchett 在 2008 年發(fā)表給 ACM 的文章,是一篇解釋 BASE 原則,或者說最終一致性的經(jīng)典文章。文中討論了 BASE 與 ACID 原則在保證數(shù)據(jù)一致性的基本差異。
如果 ACID 為分區(qū)的數(shù)據(jù)庫提供一致性的選擇,那么如何實現(xiàn)可用性呢?答案是
BASE (basically available, soft state, eventually consistent)
BASE 的可用性是通過支持局部故障而不是系統(tǒng)全局故障來實現(xiàn)的。下面是一個簡單的例子:如果將用戶分區(qū)在 5 個數(shù)據(jù)庫服務器上,BASE 設計鼓勵類似的處理方式,一個用戶數(shù)據(jù)庫的故障只影響這臺特定主機那 20% 的用戶。這里不涉及任何魔法,不過它確實可以帶來更高的可感知的系統(tǒng)可用性。
文章中描述了一個最常見的場景,如果產(chǎn)生了一筆交易,需要在交易表增加記錄,同時還要修改用戶表的金額。這兩個表屬于不同的遠程服務,所以就涉及到分布式事務一致性的問題。
文中提出了一個經(jīng)典的解決方法,將主要修改操作以及更新用戶表的消息放在一個本地事務來完成。同時為了避免重復消費用戶表消息帶來的問題,達到多次重試的冪等性,增加一個更新記錄表 updates_applied 來記錄已經(jīng)處理過的消息。
系統(tǒng)的執(zhí)行偽代碼如下
基于以上方法,在第一階段,通過本地的數(shù)據(jù)庫的事務保障,增加了 transaction 表及消息隊列 。
在第二階段,分別讀出消息隊列(但不刪除),通過判斷更新記錄表 updates_applied 來檢測相關(guān)記錄是否被執(zhí)行,未被執(zhí)行的記錄會修改 user 表,然后增加一條操作記錄到 updates_applied,事務執(zhí)行成功之后再刪除隊列。
通過以上方法,達到了分布式系統(tǒng)的最終一致性。進一步了解 eBay 的方案可以參考文末鏈接。
3. 去哪兒網(wǎng)分布式事務方案
隨著業(yè)務規(guī)模不斷地擴大,電商網(wǎng)站一般都要面臨拆分之路。就是將原來一個單體應用拆分成多個不同職責的子系統(tǒng)。比如以前可能將面向用戶、客戶和運營的功能都放在一個系統(tǒng)里,現(xiàn)在拆分為訂單中心、代理商管理、運營系統(tǒng)、報價中心、庫存管理等多個子系統(tǒng)。
拆分首先要面臨的是什么呢?
最開始的單體應用所有功能都在一起,存儲也在一起。比如運營要取消某個訂單,那直接去更新訂單表狀態(tài),然后更新庫存表就 ok 了。因為是單體應用,庫在一起,這些都可以在一個事務里,由關(guān)系數(shù)據(jù)庫來保證一致性。
但拆分之后就不同了,不同的子系統(tǒng)都有自己的存儲。比如訂單中心就只管理自己的訂單庫,而庫存管理也有自己的庫。那么運營系統(tǒng)取消訂單的時候就是通過接口調(diào)用等方式來調(diào)用訂單中心和庫存管理的服務了,而不是直接去操作庫。這就涉及一個『分布式事務』的問題。
分布式事務有兩種解決方式
1. 優(yōu)先使用異步消息。
上文已經(jīng)說過,使用異步消息 Consumer 端需要實現(xiàn)冪等。
冪等有兩種方式,一種方式是業(yè)務邏輯保證冪等。比如接到支付成功的消息訂單狀態(tài)變成支付完成,如果當前狀態(tài)是支付完成,則再收到一個支付成功的消息則說明消息重復了,直接作為消息成功處理。
另外一種方式如果業(yè)務邏輯無法保證冪等,則要增加一個去重表或者類似的實現(xiàn)。對于 producer 端在業(yè)務數(shù)據(jù)庫的同實例上放一個消息庫,發(fā)消息和業(yè)務操作在同一個本地事務里。發(fā)消息的時候消息并不立即發(fā)出,而是向消息庫插入一條消息記錄,然后在事務提交的時候再異步將消息發(fā)出,發(fā)送消息如果成功則將消息庫里的消息刪除,如果遇到消息隊列服務異?;蚓W(wǎng)絡問題,消息沒有成功發(fā)出那么消息就留在這里了,會有另外一個服務不斷地將這些消息掃出重新發(fā)送。
2. 有的業(yè)務不適合異步消息的方式,事務的各個參與方都需要同步的得到結(jié)果。這種情況的實現(xiàn)方式其實和上面類似,每個參與方的本地業(yè)務庫的同實例上面放一個事務記錄庫。
比如 A 同步調(diào)用 B,C。A 本地事務成功的時候更新本地事務記錄狀態(tài),B 和 C 同樣。如果有一次 A 調(diào)用 B 失敗了,這個失敗可能是 B 真的失敗了,也可能是調(diào)用超時,實際 B 成功。則由一個中心服務對比三方的事務記錄表,做一個最終決定。假設現(xiàn)在三方的事務記錄是 A 成功,B 失敗,C 成功。那么最終決定有兩種方式,根據(jù)具體場景:
重試 B,直到 B 成功,事務記錄表里記錄了各項調(diào)用參數(shù)等信息;
執(zhí)行 A 和 B 的補償操作(一種可行的補償方式是回滾)。
對 b 場景做一個特殊說明:比如 B 是扣庫存服務,在第一次調(diào)用的時候因為某種原因失敗了,但是重試的時候庫存已經(jīng)變?yōu)?0,無法重試成功,這個時候只有回滾 A 和 C 了。
那么可能有人覺得在業(yè)務庫的同實例里放消息庫或事務記錄庫,會對業(yè)務侵入,業(yè)務還要關(guān)心這個庫,是否一個合理的設計?
實際上可以依靠運維的手段來簡化開發(fā)的侵入,我們的方法是讓 DBA 在公司所有 MySQL 實例上預初始化這個庫,通過框架層(消息的客戶端或事務 RPC 框架)透明的在背后操作這個庫,業(yè)務開發(fā)人員只需要關(guān)心自己的業(yè)務邏輯,不需要直接訪問這個庫。
總結(jié)起來,其實兩種方式的根本原理是類似的,也就是將分布式事務轉(zhuǎn)換為多個本地事務,然后依靠重試等方式達到最終一致性。
4. 蘑菇街交易創(chuàng)建過程中的分布式一致性方案
交易創(chuàng)建的一般性流程
我們把交易創(chuàng)建流程抽象出一系列可擴展的功能點,每個功能點都可以有多個實現(xiàn)(具體的實現(xiàn)之間有組合/互斥關(guān)系)。把各個功能點按照一定流程串起來,就完成了交易創(chuàng)建的過程。
面臨的問題
每個功能點的實現(xiàn)都可能會依賴外部服務。那么如何保證各個服務之間的數(shù)據(jù)是一致的呢?比如鎖定優(yōu)惠券服務調(diào)用超時了,不能確定到底有沒有鎖券成功,該如何處理?再比如鎖券成功了,但是扣減庫存失敗了,該如何處理?
方案選型
服務依賴過多,會帶來管理復雜性增加和穩(wěn)定性風險增大的問題。試想如果我們強依賴 10 個服務,9 個都執(zhí)行成功了,最后一個執(zhí)行失敗了,那么是不是前面 9 個都要回滾掉?這個成本還是非常高的。
所以在拆分大的流程為多個小的本地事務的前提下,對于非實時、非強一致性的關(guān)聯(lián)業(yè)務寫入,在本地事務執(zhí)行成功后,我們選擇發(fā)消息通知、關(guān)聯(lián)事務異步化執(zhí)行的方案。
消息通知往往不能保證 100% 成功;且消息通知后,接收方業(yè)務是否能執(zhí)行成功還是未知數(shù)。前者問題可以通過重試解決;后者可以選用事務消息來保證。
但是事務消息框架本身會給業(yè)務代碼帶來侵入性和復雜性,所以我們選擇基于 DB 事件變化通知到 MQ 的方式做系統(tǒng)間解耦,通過訂閱方消費 MQ 消息時的 ACK 機制,保證消息一定消費成功,達到最終一致性。由于消息可能會被重發(fā),消息訂閱方業(yè)務邏輯處理要做好冪等保證。
所以目前只剩下需要實時同步做、有強一致性要求的業(yè)務場景了。在交易創(chuàng)建過程中,鎖券和扣減庫存是這樣的兩個典型場景。
要保證多個系統(tǒng)間數(shù)據(jù)一致,乍一看,必須要引入分布式事務框架才能解決。但引入非常重的類似二階段提交分布式事務框架會帶來復雜性的急劇上升;在電商領(lǐng)域,絕對的強一致是過于理想化的,我們可以選擇準實時的最終一致性。
我們在交易創(chuàng)建流程中,首先創(chuàng)建一個不可見訂單,然后在同步調(diào)用鎖券和扣減庫存時,針對調(diào)用異常(失敗或者超時),發(fā)出廢單消息到MQ。如果消息發(fā)送失敗,本地會做時間階梯式的異步重試;優(yōu)惠券系統(tǒng)和庫存系統(tǒng)收到消息后,會進行判斷是否需要做業(yè)務回滾,這樣就準實時地保證了多個本地事務的最終一致性。
5. 支付寶及螞蟻金融云的分布式服務 DTS 方案
業(yè)界常用的還有支付寶的一種 xts 方案,由支付寶在 2PC 的基礎(chǔ)上改進而來。主要思路如下,大部分信息引用自官方網(wǎng)站。
分布式事務服務簡介
分布式事務服務 (Distributed Transaction Service, DTS) 是一個分布式事務框架,用來保障在大規(guī)模分布式環(huán)境下事務的最終一致性。DTS 從架構(gòu)上分為 xts-client 和 xts-server 兩部分,前者是一個嵌入客戶端應用的 JAR 包,主要負責事務數(shù)據(jù)的寫入和處理;后者是一個獨立的系統(tǒng),主要負責異常事務的恢復。
核心特性
傳統(tǒng)關(guān)系型數(shù)據(jù)庫的事務模型必須遵守 ACID 原則。在單數(shù)據(jù)庫模式下,ACID 模型能有效保障數(shù)據(jù)的完整性,但是在大規(guī)模分布式環(huán)境下,一個業(yè)務往往會跨越多個數(shù)據(jù)庫,如何保證這多個數(shù)據(jù)庫之間的數(shù)據(jù)一致性,需要其他行之有效的策略。在 JavaEE 規(guī)范中使用 2PC (2 Phase Commit, 兩階段提交) 來處理跨 DB 環(huán)境下的事務問題,但是 2PC 是反可伸縮模式,也就是說,在事務處理過程中,參與者需要一直持有資源直到整個分布式事務結(jié)束。這樣,當業(yè)務規(guī)模達到千萬級以上時,2PC 的局限性就越來越明顯,系統(tǒng)可伸縮性會變得很差?;诖?,我們采用 BASE 的思想實現(xiàn)了一套類似 2PC 的分布式事務方案,這就是 DTS。DTS在充分保障分布式環(huán)境下高可用性、高可靠性的同時兼顧數(shù)據(jù)一致性的要求,其最大的特點是保證數(shù)據(jù)最終一致 (Eventually consistent)。
簡單的說,DTS 框架有如下特性:
最終一致:事務處理過程中,會有短暫不一致的情況,但通過恢復系統(tǒng),可以讓事務的數(shù)據(jù)達到最終一致的目標。
協(xié)議簡單:DTS 定義了類似 2PC 的標準兩階段接口,業(yè)務系統(tǒng)只需要實現(xiàn)對應的接口就可以使用 DTS 的事務功能。
與 RPC 服務協(xié)議無關(guān):在 SOA 架構(gòu)下,一個或多個 DB 操作往往被包裝成一個一個的 Service,Service 與 Service 之間通過 RPC 協(xié)議通信。DTS 框架構(gòu)建在 SOA 架構(gòu)上,與底層協(xié)議無關(guān)。
與底層事務實現(xiàn)無關(guān): DTS 是一個抽象的基于 Service 層的概念,與底層事務實現(xiàn)無關(guān),也就是說在 DTS 的范圍內(nèi),無論是關(guān)系型數(shù)據(jù)庫 MySQL,Oracle,還是 KV 存儲 MemCache,或者列存數(shù)據(jù)庫 HBase,只要將對其的操作包裝成 DTS 的參與者,就可以接入到 DTS 事務范圍內(nèi)。
以下是分布式事務框架的流程圖
實現(xiàn)
一個完整的業(yè)務活動由一個主業(yè)務服務與若干從業(yè)務服務組成。
主業(yè)務服務負責發(fā)起并完成整個業(yè)務活動。
從業(yè)務服務提供 TCC 型業(yè)務操作。
業(yè)務活動管理器控制業(yè)務活動的一致性,它登記業(yè)務活動中的操作,并在活動提交時確認所有的兩階段事務的 confirm 操作,在業(yè)務活動取消時調(diào)用所有兩階段事務的 cancel 操作?!?/p>
與 2PC 協(xié)議比較
沒有單獨的 Prepare 階段,降低協(xié)議成本
系統(tǒng)故障容忍度高,恢復簡單
6. 農(nóng)信網(wǎng)數(shù)據(jù)一致性方案
1. 電商業(yè)務
公司的支付部門,通過接入其它第三方支付系統(tǒng)來提供支付服務給業(yè)務部門,支付服務是一個基于 Dubbo 的 RPC 服務。
對于業(yè)務部門來說,電商部門的訂單支付,需要調(diào)用
支付平臺的支付接口來處理訂單;
同時需要調(diào)用積分中心的接口,按照業(yè)務規(guī)則,給用戶增加積分。
從業(yè)務規(guī)則上需要同時保證業(yè)務數(shù)據(jù)的實時性和一致性,也就是支付成功必須加積分。
我們采用的方式是同步調(diào)用,首先處理本地事務業(yè)務。考慮到積分業(yè)務比較單一且業(yè)務影響低于支付,由積分平臺提供增加與回撤接口。
具體的流程是先調(diào)用積分平臺增加用戶積分,再調(diào)用支付平臺進行支付處理,如果處理失敗,catch 方法調(diào)用積分平臺的回撤方法,將本次處理的積分訂單回撤。
(點擊圖片可以全屏縮放)
2. 用戶信息變更
公司的用戶信息,統(tǒng)一由用戶中心維護,而用戶信息的變更需要同步給各業(yè)務子系統(tǒng),業(yè)務子系統(tǒng)再根據(jù)變更內(nèi)容,處理各自業(yè)務。用戶中心作為 MQ 的 producer,添加通知給 MQ。APP Server 訂閱該消息,同步本地數(shù)據(jù)信息,再處理相關(guān)業(yè)務比如 APP 退出下線等。
我們采用異步消息通知機制,目前主要使用 ActiveMQ,基于 Virtual Topic 的訂閱方式,保證單個業(yè)務集群訂閱的單次消費。
感謝各位的閱讀,以上就是“保證服務器分布式系統(tǒng)數(shù)據(jù)一致性的方法有哪些”的內(nèi)容了,經(jīng)過本文的學習后,相信大家對保證服務器分布式系統(tǒng)數(shù)據(jù)一致性的方法有哪些這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。