保證分布式系統數據一致性的6種方案(轉載)

問題的起源

在電商等業務中,系統一般由多個獨立的服務組成,如何解決分布式調用時候數據的一致性?

具體業務場景如下,比如一個業務操作,如果同時調用服務 A、B、C,需要滿足要么同時成功;要么同時失敗。A、B、C 可能是多個不同部門開發、部署在不同服務器上的遠程服務。

在分布式系統來說,如果不想犧牲一致性,CAP 理論告訴我們只能放棄可用性,這顯然不能接受。為了便于討論問題,先簡單介紹下數據一致性的基礎理論。

強一致

當更新操作完成之后,任何多個后續進程或者線程的訪問都會返回最新的更新過的值。這種是對用戶最友好的,就是用戶上一次寫什么,下一次就保證能讀到什么。根據 CAP 理論,這種實現需要犧牲可用性。

弱一致性

系統并不保證續進程或者線程的訪問都會返回最新的更新過的值。系統在數據寫入成功之后,不承諾立即可以讀到最新寫入的值,也不會具體的承諾多久之后可以讀到。

最終一致性

弱一致性的特定形式。系統保證在沒有后續更新的前提下,系統最終返回上一次更新操作的值。在沒有故障發生的前提下,不一致窗口的時間主要受通信延遲,系統負載和復制副本的個數影響。DNS 是一個典型的最終一致性系統。

在工程實踐上,為了保障系統的可用性,互聯網系統大多將強一致性需求轉換成最終一致性的需求,并通過系統執行冪等性的保證,保證數據的最終一致性。但在電商等場景中,對于數據一致性的解決方法和常見的互聯網系統(如 MySQL 主從同步)又有一定區別,群友的討論分成以下 6 種解決方案。

方案1、規避分布式事務——業務整合

業務整合方案主要采用將接口整合到本地執行的方法。拿問題場景來說,則可以將服務 A、B、C 整合為一個服務 D 給業務,這個服務 D 再通過轉換為本地事務的方式,比如服務 D 包含本地服務和服務 E,而服務 E 是本地服務 A ~ C 的整合。

優點:解決(規避)了分布式事務。

缺點:顯而易見,把本來規劃拆分好的業務,又耦合到了一起,業務職責不清晰,不利于維護。

由于這個方法存在明顯缺點,通常不建議使用。

方案2、經典方案 - eBay 模式

此方案的核心是將需要分布式處理的任務通過消息日志的方式來異步執行。消息日志可以存儲到本地文本、數據庫或消息隊列,再通過業務規則自動或人工發起重試。人工重試更多的是應用于支付場景,通過對賬系統對事后問題的處理。

消息日志方案的核心是保證服務接口的冪等性。

考慮到網絡通訊失敗、數據丟包等原因,如果接口不能保證冪等性,數據的唯一性將很難保證。

eBay 方式的主要思路如下。

Base:一種 Acid 的替代方案

此方案是 eBay 的架構師 Dan Pritchett 在 2008 年發表給 ACM 的文章,是一篇解釋 BASE 原則,或者說最終一致性的經典文章。文中討論了 BASE 與 ACID 原則在保證數據一致性的基本差異。

如果 ACID 為分區的數據庫提供一致性的選擇,那么如何實現可用性呢?答案是

BASE (basically available, soft state, eventually consistent)

BASE 的可用性是通過支持局部故障而不是系統全局故障來實現的。下面是一個簡單的例子:如果將用戶分區在 5 個數據庫服務器上,BASE 設計鼓勵類似的處理方式,一個用戶數據庫的故障只影響這臺特定主機那 20% 的用戶。這里不涉及任何魔法,不過它確實可以帶來更高的可感知的系統可用性。

文章中描述了一個最常見的場景,如果產生了一筆交易,需要在交易表增加記錄,同時還要修改用戶表的金額。這兩個表屬于不同的遠程服務,所以就涉及到分布式事務一致性的問題。

image

文中提出了一個經典的解決方法,將主要修改操作以及更新用戶表的消息放在一個本地事務來完成。同時為了避免重復消費用戶表消息帶來的問題,達到多次重試的冪等性,增加一個更新記錄表 updates_applied 來記錄已經處理過的消息。

image

系統的執行偽代碼如下

image

(點擊可全屏縮放圖片)

基于以上方法,在第一階段,通過本地的數據庫的事務保障,增加了 transaction 表及消息隊列 。

在第二階段,分別讀出消息隊列(但不刪除),通過判斷更新記錄表 updates_applied 來檢測相關記錄是否被執行,未被執行的記錄會修改 user 表,然后增加一條操作記錄到 updates_applied,事務執行成功之后再刪除隊列。

通過以上方法,達到了分布式系統的最終一致性。進一步了解 eBay 的方案可以參考文末鏈接。

方案3、去哪兒網分布式事務方案

隨著業務規模不斷地擴大,電商網站一般都要面臨拆分之路。就是將原來一個單體應用拆分成多個不同職責的子系統。比如以前可能將面向用戶、客戶和運營的功能都放在一個系統里,現在拆分為訂單中心、代理商管理、運營系統、報價中心、庫存管理等多個子系統。

拆分首先要面臨的是什么呢?

最開始的單體應用所有功能都在一起,存儲也在一起。比如運營要取消某個訂單,那直接去更新訂單表狀態,然后更新庫存表就 ok 了。因為是單體應用,庫在一起,這些都可以在一個事務里,由關系數據庫來保證一致性。

但拆分之后就不同了,不同的子系統都有自己的存儲。比如訂單中心就只管理自己的訂單庫,而庫存管理也有自己的庫。那么運營系統取消訂單的時候就是通過接口調用等方式來調用訂單中心和庫存管理的服務了,而不是直接去操作庫。這就涉及一個『分布式事務』的問題。

image

分布式事務有兩種解決方式

1. 優先使用異步消息。

上文已經說過,使用異步消息 Consumer 端需要實現冪等。

冪等有兩種方式,一種方式是業務邏輯保證冪等。比如接到支付成功的消息訂單狀態變成支付完成,如果當前狀態是支付完成,則再收到一個支付成功的消息則說明消息重復了,直接作為消息成功處理。

另外一種方式如果業務邏輯無法保證冪等,則要增加一個去重表或者類似的實現。對于 producer 端在業務數據庫的同實例上放一個消息庫,發消息和業務操作在同一個本地事務里。發消息的時候消息并不立即發出,而是向消息庫插入一條消息記錄,然后在事務提交的時候再異步將消息發出,發送消息如果成功則將消息庫里的消息刪除,如果遇到消息隊列服務異常或網絡問題,消息沒有成功發出那么消息就留在這里了,會有另外一個服務不斷地將這些消息掃出重新發送。

2. 有的業務不適合異步消息的方式,事務的各個參與方都需要同步的得到結果。這種情況的實現方式其實和上面類似,每個參與方的本地業務庫的同實例上面放一個事務記錄庫。

比如 A 同步調用 B,C。A 本地事務成功的時候更新本地事務記錄狀態,B 和 C 同樣。如果有一次 A 調用 B 失敗了,這個失敗可能是 B 真的失敗了,也可能是調用超時,實際 B 成功。則由一個中心服務對比三方的事務記錄表,做一個最終決定。假設現在三方的事務記錄是 A 成功,B 失敗,C 成功。那么最終決定有兩種方式,根據具體場景:

  1. 重試 B,直到 B 成功,事務記錄表里記錄了各項調用參數等信息;

  2. 執行 A 和 B 的補償操作(一種可行的補償方式是回滾)。

對 b 場景做一個特殊說明:比如 B 是扣庫存服務,在第一次調用的時候因為某種原因失敗了,但是重試的時候庫存已經變為 0,無法重試成功,這個時候只有回滾 A 和 C 了。

那么可能有人覺得在業務庫的同實例里放消息庫或事務記錄庫,會對業務侵入,業務還要關心這個庫,是否一個合理的設計?

實際上可以依靠運維的手段來簡化開發的侵入,我們的方法是讓 DBA 在公司所有 MySQL 實例上預初始化這個庫,通過框架層(消息的客戶端或事務 RPC 框架)透明的在背后操作這個庫,業務開發人員只需要關心自己的業務邏輯,不需要直接訪問這個庫。

總結起來,其實兩種方式的根本原理是類似的,也就是將分布式事務轉換為多個本地事務,然后依靠重試等方式達到最終一致性

方案4、蘑菇街交易創建過程中的分布式一致性方案

交易創建的一般性流程

我們把交易創建流程抽象出一系列可擴展的功能點,每個功能點都可以有多個實現(具體的實現之間有組合/互斥關系)。把各個功能點按照一定流程串起來,就完成了交易創建的過程。

image

面臨的問題

每個功能點的實現都可能會依賴外部服務。那么如何保證各個服務之間的數據是一致的呢?比如鎖定優惠券服務調用超時了,不能確定到底有沒有鎖券成功,該如何處理?再比如鎖券成功了,但是扣減庫存失敗了,該如何處理?

方案選型

服務依賴過多,會帶來管理復雜性增加和穩定性風險增大的問題。試想如果我們強依賴 10 個服務,9 個都執行成功了,最后一個執行失敗了,那么是不是前面 9 個都要回滾掉?這個成本還是非常高的。

所以在拆分大的流程為多個小的本地事務的前提下,對于非實時、非強一致性的關聯業務寫入,在本地事務執行成功后,我們選擇發消息通知、關聯事務異步化執行的方案。

消息通知往往不能保證 100% 成功;且消息通知后,接收方業務是否能執行成功還是未知數。前者問題可以通過重試解決;后者可以選用事務消息來保證。

但是事務消息框架本身會給業務代碼帶來侵入性和復雜性,所以我們選擇基于 DB 事件變化通知到 MQ 的方式做系統間解耦,通過訂閱方消費 MQ 消息時的 ACK 機制,保證消息一定消費成功,達到最終一致性。由于消息可能會被重發,消息訂閱方業務邏輯處理要做好冪等保證。

所以目前只剩下需要實時同步做、有強一致性要求的業務場景了。在交易創建過程中,鎖券和扣減庫存是這樣的兩個典型場景。

要保證多個系統間數據一致,乍一看,必須要引入分布式事務框架才能解決。但引入非常重的類似二階段提交分布式事務框架會帶來復雜性的急劇上升;在電商領域,絕對的強一致是過于理想化的,我們可以選擇準實時的最終一致性。

我們在交易創建流程中,首先創建一個不可見訂單,然后在同步調用鎖券和扣減庫存時,針對調用異常(失敗或者超時),發出廢單消息到MQ。如果消息發送失敗,本地會做時間階梯式的異步重試;優惠券系統和庫存系統收到消息后,會進行判斷是否需要做業務回滾,這樣就準實時地保證了多個本地事務的最終一致性。

image

方案5、支付寶及螞蟻金融云的分布式服務 DTS 方案

業界常用的還有支付寶的一種 xts 方案,由支付寶在 2PC 的基礎上改進而來。主要思路如下,大部分信息引用自官方網站。

分布式事務服務簡介

分布式事務服務 (Distributed Transaction Service, DTS) 是一個分布式事務框架,用來保障在大規模分布式環境下事務的最終一致性。DTS 從架構上分為 xts-client 和 xts-server 兩部分,前者是一個嵌入客戶端應用的 JAR 包,主要負責事務數據的寫入和處理;后者是一個獨立的系統,主要負責異常事務的恢復。

核心特性

傳統關系型數據庫的事務模型必須遵守 ACID 原則。在單數據庫模式下,ACID 模型能有效保障數據的完整性,但是在大規模分布式環境下,一個業務往往會跨越多個數據庫,如何保證這多個數據庫之間的數據一致性,需要其他行之有效的策略。在 JavaEE 規范中使用 2PC (2 Phase Commit, 兩階段提交) 來處理跨 DB 環境下的事務問題,但是 2PC 是反可伸縮模式,也就是說,在事務處理過程中,參與者需要一直持有資源直到整個分布式事務結束。這樣,當業務規模達到千萬級以上時,2PC 的局限性就越來越明顯,系統可伸縮性會變得很差。基于此,我們采用 BASE 的思想實現了一套類似 2PC 的分布式事務方案,這就是 DTS。DTS在充分保障分布式環境下高可用性、高可靠性的同時兼顧數據一致性的要求,其最大的特點是保證數據最終一致 (Eventually consistent)。

簡單的說,DTS 框架有如下特性:

  • 最終一致:事務處理過程中,會有短暫不一致的情況,但通過恢復系統,可以讓事務的數據達到最終一致的目標。

  • 協議簡單:DTS 定義了類似 2PC 的標準兩階段接口,業務系統只需要實現對應的接口就可以使用 DTS 的事務功能。

  • 與 RPC 服務協議無關:在 SOA 架構下,一個或多個 DB 操作往往被包裝成一個一個的 Service,Service 與 Service 之間通過 RPC 協議通信。DTS 框架構建在 SOA 架構上,與底層協議無關。

  • 與底層事務實現無關: DTS 是一個抽象的基于 Service 層的概念,與底層事務實現無關,也就是說在 DTS 的范圍內,無論是關系型數據庫 MySQL,Oracle,還是 KV 存儲 MemCache,或者列存數據庫 HBase,只要將對其的操作包裝成 DTS 的參與者,就可以接入到 DTS 事務范圍內。

以下是分布式事務框架的流程圖

image

實現

  1. 一個完整的業務活動由一個主業務服務與若干從業務服務組成。

  2. 主業務服務負責發起并完成整個業務活動。

  3. 從業務服務提供 TCC 型業務操作。

  4. 業務活動管理器控制業務活動的一致性,它登記業務活動中的操作,并在活動提交時確認所有的兩階段事務的 confirm 操作,在業務活動取消時調用所有兩階段事務的 cancel 操作。”

與 2PC 協議比較

  1. 沒有單獨的 Prepare 階段,降低協議成本

  2. 系統故障容忍度高,恢復簡單

方案6、農信網數據一致性方案

1. 電商業務

公司的支付部門,通過接入其它第三方支付系統來提供支付服務給業務部門,支付服務是一個基于 Dubbo 的 RPC 服務。

對于業務部門來說,電商部門的訂單支付,需要調用

  1. 支付平臺的支付接口來處理訂單;

  2. 同時需要調用積分中心的接口,按照業務規則,給用戶增加積分。

從業務規則上需要同時保證業務數據的實時性和一致性,也就是支付成功必須加積分。

我們采用的方式是同步調用,首先處理本地事務業務。考慮到積分業務比較單一且業務影響低于支付,由積分平臺提供增加與回撤接口。

具體的流程是先調用積分平臺增加用戶積分,再調用支付平臺進行支付處理,如果處理失敗,catch 方法調用積分平臺的回撤方法,將本次處理的積分訂單回撤。

image

(點擊圖片可以全屏縮放)

2. 用戶信息變更

公司的用戶信息,統一由用戶中心維護,而用戶信息的變更需要同步給各業務子系統,業務子系統再根據變更內容,處理各自業務。用戶中心作為 MQ 的 producer,添加通知給 MQ。APP Server 訂閱該消息,同步本地數據信息,再處理相關業務比如 APP 退出下線等。

我們采用異步消息通知機制,目前主要使用 ActiveMQ,基于 Virtual Topic 的訂閱方式,保證單個業務集群訂閱的單次消費。

image

總結

分布式服務對衍生的配套系統要求比較多,特別是我們基于消息、日志的最終一致性方案,需要考慮消息的積壓、消費情況、監控、報警等。

參考資料

  • Base: An Acid Alternative (eBay 方案)

In partitioned databases, trading some consistency for availability can lead to dramatic improvements in scalability.

英文版 : http://queue.acm.org/detail.cfm?id=1394128

中文版: http://article.yeeyan.org/view/167444/125572

原文地址:http://weibo.com/ttarticle/p/show?id=2309403965965003062676

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,559評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,442評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,581評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,922評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,096評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,639評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,374評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,591評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,789評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,322評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,554評論 2 379

推薦閱讀更多精彩內容