原文:http://weibo.com/ttarticle/p/show?id=2309403965965003062676數據庫
編者按:本文由「高可用架構後花園」羣討論整理而成。服務器
有人的地方,就有江湖網絡
有江湖的地方,就有紛爭架構
問題的起源app
在電商等業務中,系統通常由多個獨立的服務組成,如何解決分佈式調用時候數據的一致性? 框架
具體業務場景以下,好比一個業務操做,若是同時調用服務 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% 的用戶。這裏不涉及任何魔法,不過它確實能夠帶來更高的可感知的系統可用性。
文章中描述了一個最多見的場景,若是產生了一筆交易,須要在交易表增長記錄,同時還要修改用戶表的金額。這兩個表屬於不一樣的遠程服務,因此就涉及到分佈式事務一致性的問題。
文中提出了一個經典的解決方法,將主要修改操做以及更新用戶表的消息放在一個本地事務來完成。同時爲了不重複消費用戶表消息帶來的問題,達到屢次重試的冪等性,增長一個更新記錄表 updates_applied 來記錄已經處理過的消息。
系統的執行僞代碼以下
(點擊可全屏縮放圖片)
基於以上方法,在第一階段,經過本地的數據庫的事務保障,增長了 transaction 表及消息隊列 。
在第二階段,分別讀出消息隊列(但不刪除),經過判斷更新記錄表 updates_applied 來檢測相關記錄是否被執行,未被執行的記錄會修改 user 表,而後增長一條操做記錄到 updates_applied,事務執行成功以後再刪除隊列。
經過以上方法,達到了分佈式系統的最終一致性。進一步瞭解 eBay 的方案能夠參考文末連接。
3. 去哪兒網分佈式事務方案
隨着業務規模不斷地擴大,電商網站通常都要面臨拆分之路。就是將原來一個單體應用拆分紅多個不一樣職責的子系統。好比之前可能將面向用戶、客戶和運營的功能都放在一個系統裏,如今拆分爲訂單中心、代理商管理、運營系統、報價中心、庫存管理等多個子系統。
拆分首先要面臨的是什麼呢?
最開始的單體應用全部功能都在一塊兒,存儲也在一塊兒。好比運營要取消某個訂單,那直接去更新訂單表狀態,而後更新庫存表就 ok 了。由於是單體應用,庫在一塊兒,這些均可以在一個事務裏,由關係數據庫來保證一致性。
但拆分以後就不一樣了,不一樣的子系統都有本身的存儲。好比訂單中心就只管理本身的訂單庫,而庫存管理也有本身的庫。那麼運營系統取消訂單的時候就是經過接口調用等方式來調用訂單中心和庫存管理的服務了,而不是直接去操做庫。這就涉及一個『分佈式事務』的問題。
分佈式事務有兩種解決方式
1. 優先使用異步消息。
上文已經說過,使用異步消息 Consumer 端須要實現冪等。
冪等有兩種方式,一種方式是業務邏輯保證冪等。好比接到支付成功的消息訂單狀態變成支付完成,若是當前狀態是支付完成,則再收到一個支付成功的消息則說明消息重複了,直接做爲消息成功處理。
另一種方式若是業務邏輯沒法保證冪等,則要增長一個去重表或者相似的實現。 對於 producer 端在業務數據庫的同實例上放一個消息庫,發消息和業務操做在同一個本地事務裏。發消息的時候消息並不當即發出,而是向消息庫插入一條消息記錄,而後在事務 提交的時候再異步將消息發出,發送消息若是成功則將消息庫裏的消息刪除,若是遇到消息隊列服務異常或網絡問題,消息沒有成功發出那麼消息就留在這裏了,會 有另一個服務不斷地將這些消息掃出從新發送。
2. 有的業務不適合異步消息的方式,事務的各個參與方都須要同步的獲得結果。這種狀況的實現方式其實和上面相似,每一個參與方的本地業務庫的同實例上面放一個事務記錄庫。
比 如 A 同步調用 B,C。A 本地事務成功的時候更新本地事務記錄狀態,B 和 C 一樣。若是有一次 A 調用 B 失敗了,這個失敗多是 B 真的失敗了,也多是調用超時,實際 B 成功。則由一箇中心服務對比三方的事務記錄表,作一個最終決定。假設如今三方的事務記錄是 A 成功,B 失敗,C 成功。那麼最終決定有兩種方式,根據具體場景:
重試 B,直到 B 成功,事務記錄表裏記錄了各項調用參數等信息;
執行 A 和 B 的補償操做(一種可行的補償方式是回滾)。
對 b 場景作一個特殊說明:好比 B 是扣庫存服務,在第一次調用的時候由於某種緣由失敗了,可是重試的時候庫存已經變爲 0,沒法重試成功,這個時候只有回滾 A 和 C 了。
那麼可能有人以爲在業務庫的同實例裏放消息庫或事務記錄庫,會對業務侵入,業務還要關心這個庫,是否一個合理的設計?
實際上能夠依靠運維的手段來簡化開發的侵入,咱們的方法是讓 DBA 在公司全部 MySQL 實例上預初始化這個庫,經過框架層(消息的客戶端或事務 RPC 框架)透明的在背後操做這個庫,業務開發人員只須要關心本身的業務邏輯,不須要直接訪問這個庫。
總結起來,其實兩種方式的根本原理是相似的,也就是將分佈式事務轉換爲多個本地事務,而後依靠重試等方式達到最終一致性。
4. 蘑菇街交易建立過程當中的分佈式一致性方案
交易建立的通常性流程
咱們把交易建立流程抽象出一系列可擴展的功能點,每一個功能點均可以有多個實現(具體的實現之間有組合/互斥關係)。把各個功能點按照必定流程串起來,就完成了交易建立的過程。
面臨的問題
每一個功能點的實現均可能會依賴外部服務。那麼如何保證各個服務之間的數據是一致的呢?好比鎖定優惠券服務調用超時了,不能肯定到底有沒有鎖券成功,該如何處理?再好比鎖券成功了,可是扣減庫存失敗了,該如何處理?
方案選型
服務依賴過多,會帶來管理複雜性增長和穩定性風險增大的問題。試想若是咱們強依賴 10 個服務,9 個都執行成功了,最後一個執行失敗了,那麼是否是前面 9 個都要回滾掉?這個成本仍是很是高的。
因此在拆分大的流程爲多個小的本地事務的前提下,對於非實時、非強一致性的關聯業務寫入,在本地事務執行成功後,咱們選擇發消息通知、關聯事務異步化執行的方案。
消息通知每每不能保證 100% 成功;且消息通知後,接收方業務是否能執行成功仍是未知數。前者問題能夠經過重試解決;後者能夠選用事務消息來保證。
可是事務消息框架自己會給業務代碼帶來侵入性和複雜性,因此咱們選擇基於 DB 事件變化通知到 MQ 的方式作系統間解耦,經過訂閱方消費 MQ 消息時的 ACK 機制,保證消息必定消費成功,達到最終一致性。因爲消息可能會被重發,消息訂閱方業務邏輯處理要作好冪等保證。
因此目前只剩下須要實時同步作、有強一致性要求的業務場景了。在交易建立過程當中,鎖券和扣減庫存是這樣的兩個典型場景。
要保證多個系統間數據一致,乍一看,必需要引入分佈式事務框架才能解決。但引入很是重的相似二階段提交分佈式事務框架會帶來複雜性的急劇上升;在電商領域,絕對的強一致是過於理想化的,咱們能夠選擇準實時的最終一致性。
咱們在交易建立流程中,首先建立一個不可見訂單,而後在同步調用鎖券和扣減庫存時,針對調用異常(失敗或者超時),發出廢單消息到MQ。若是消息發送失敗,本地會作時間階梯式的異步重試;優惠券系統和庫存系統收到消息後,會進行判斷是否須要作業務回滾,這樣就準實時地保證了多個本地事務的最終一致性。
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 事務範圍內。
如下是分佈式事務框架的流程圖
實現
一個完整的業務活動由一個主業務服務與若干從業務服務組成。
主業務服務負責發起並完成整個業務活動。
從業務服務提供 TCC 型業務操做。
業務活動管理器控制業務活動的一致性,它登記業務活動中的操做,並在活動提交時確認全部的兩階段事務的 confirm 操做,在業務活動取消時調用全部兩階段事務的 cancel 操做。」
與 2PC 協議比較
沒有單獨的 Prepare 階段,下降協議成本
系統故障容忍度高,恢復簡單
6. 農信網數據一致性方案
1. 電商業務
公司的支付部門,經過接入其它第三方支付系統來提供支付服務給業務部門,支付服務是一個基於 Dubbo 的 RPC 服務。
對於業務部門來講,電商部門的訂單支付,須要調用
支付平臺的支付接口來處理訂單;
同時須要調用積分中心的接口,按照業務規則,給用戶增長積分。
從業務規則上須要同時保證業務數據的實時性和一致性,也就是支付成功必須加積分。
咱們採用的方式是同步調用,首先處理本地事務業務。考慮到積分業務比較單一且業務影響低於支付,由積分平臺提供增長與回撤接口。
具體的流程是先調用積分平臺增長用戶積分,再調用支付平臺進行支付處理,若是處理失敗,catch 方法調用積分平臺的回撤方法,將本次處理的積分訂單回撤。
(點擊圖片能夠全屏縮放)
2. 用戶信息變動
公 司的用戶信息,統一由用戶中心維護,而用戶信息的變動須要同步給各業務子系統,業務子系統再根據變動內容,處理各自業務。用戶中心做爲 MQ 的 producer,添加通知給 MQ。APP Server 訂閱該消息,同步本地數據信息,再處理相關業務好比 APP 退出下線等。
咱們採用異步消息通知機制,目前主要使用 ActiveMQ,基於 Virtual Topic 的訂閱方式,保證單個業務集羣訂閱的單次消費。
總結
分佈式服務對衍生的配套系統要求比較多,特別是咱們基於消息、日誌的最終一致性方案,須要考慮消息的積壓、消費狀況、監控、報警等。