分佈式事務是指事務的參與者、支持事務的服務器、資源服務器以及事務管理器分別位於不一樣的分佈式系統的不一樣節點之上。html
當咱們的單個數據庫的性能產生瓶頸的時候,咱們可能會對數據庫進行分區,這裏所說的分區指的是物理分區,分區以後可能不一樣的庫就處於不一樣的服務器上了,這個時候單個數據庫的ACID已經不能適應這種狀況了,而在這種ACID的集羣環境下,再想保證集羣的ACID幾乎是很難達到,或者即便能達到那麼效率和性能會大幅降低,最爲關鍵的是再很難擴展新的分區了,這個時候若是再追求集羣的ACID會致使咱們的系統變得不好。java
咱們假設有以下一個架構,這是一個簡單的電商架構平臺,兩個應用節點,一個數據庫,一個負載均衡器。這個架構下,天天會產生將近 100W 的訂單量。那麼一個月的數據量就會超過 3000W。而隨着數據量的不斷擴大,對於訂單表的相關查詢操做的性能開銷就愈來愈大。而且響應耗時也愈來愈長。這個時候咱們須要考慮到數據庫的優化問題。也就是對數據庫進行分表分庫,達到分攤數據庫壓力以及減小數據庫單表數據量的目的。算法
分庫分表之後,一方面分擔了單庫帶來的性能壓力;另外一方面,減小了單表的數據量。完美的解決了咱們遇到的性能問題。可是,隨着而來的又有另外的問題。數據庫
Ø 好比有這樣一個場景,訂單支付成功之後須要扣減庫存。在數據庫分庫分表以前,全部數據都在同一個庫裏面,能夠經過事務操做就很容易達到數據一致性的目的。可是在數據庫作了拆分後,訂單狀態更新是屬於訂單的數據庫,而庫存扣減是屬於庫存的數據庫。本來單庫的事務操做就變成了多庫的事務操做。可是每一個庫的事務只有本身知道,訂單庫並不知道庫存庫的事務執行結果,庫存庫也不知道訂單庫的修改結果。因此就形成了分佈式事務的問題。其實也叫分佈式數據一致性。編程
X/Open DTP(X/Open Distributed Transaction Processing Reference Model) 是X/Open 這個組織定義的一套分佈式事務的標準,也就是定義了規範和 API 接口,由各個廠商進行具體的實現。這個標準提出了使用二階段提交(2PC – Two-Phase-Commit)來保證分佈式事務的完整性。後來 J2EE 也遵循了 X/OpenDTP 規範,設計並實現了 java 裏的分佈式事務編程接口規範-JTAapi
X/OpenDTP 角色:在 X/OpenDTP 事務模型中,定義了三個安全
在分佈式系統中,每個機器節點雖然都可以明確知道本身在進行事務操做過程當中的結果是成功仍是失敗,但卻沒法直接獲取到其餘分佈式節點的操做結果。所以當一個事務操做須要跨越多個分佈式節點的時候,爲了保持事務處理的 ACID 特性,就須要引入一個「協調者」(TM)來統一調度全部分佈式節點的執行邏輯,這些被調度的分佈式節點被稱爲 AP。TM 負責調度 AP 的行爲,並最終決定這些 AP 是否要把事務真正進行提交到(RM)。服務器
完成事務操做主要有如下幾個步驟:網絡
1. 參與分佈式事務的應用程序(AP)先到 TM 上註冊全局事務。架構
2. 而後各個 AP 直接在相應的資源管理器(RM)上進行事務操做。
3. 操做完成之後,各個 AP 反饋事務的處理結果給到 TM。
4. TM 收到全部 AP 的反饋之後,經過數據庫提供的 XA 接口進行數據提交或者回滾操做。
在 X/OpenDTP 模型中,一個分佈式事務所涉及的 SQL 邏輯都執行完成,併到了(RM)要最後提交事務的關鍵時刻,爲了不分佈式系統所固有的不可靠性致使提交事務意外失敗,TM 果斷決定實施兩步走的方案,這個就稱爲二階提交。
二階段提交,是計算機網絡尤爲是在數據庫領域內,爲了使基於分佈式系統架構下的全部節點在進行事務處理過程當中可以保持原子性和一致性而設計的一種算法。一般,二階段提交協議也被認爲是一種一致性協議,用來保證分佈式系統數據的一致性。目前,絕大部分的關係型數據庫都是採用二階段提交協議來完成分佈式事務處理的,利用該協議可以很是方便地完成全部分佈式事務 AP 的協調,統一決定事務的提交或回滾,從而可以有效保證分佈式數據一致性,所以 2pc 也被普遍運用在許多分佈式系統中。
第一階段:
1. 事務詢問:TM 向全部的 AP 發送事務內容,詢問是否能夠執行事務提交操做,並開始等待各AP 的響應
2. 執行事務各個 AP 節點執行事務操做,並將 Undo 和 Redo 信息記錄到事務日誌中,儘可能把提交過程當中全部消耗時間的操做和準備都提早完成確保後面 100%成功提交事務
3. 各個 AP 向 TM 反饋事務詢問的響應若是各個 AP 成功執行了事務操做,那麼就反饋給 AP yes 的響應,表示事務能夠執行;若是 AP 沒有成功執行事務,就反饋給 TM no 的響應,表示事務不能夠執行
上面這個階段有點相似 TM 組織各個 AP 對一次事務操做的投票表態過程,所以2pc 協議的第一個階段稱爲「投票階段」,即各 AP 投票表名是否須要繼續執行接下去的事務提交操做。
第二階段:
在這個階段,TM 會根據各 AP 的反饋狀況來決定最終是否能夠進行事務提交操做,正常狀況下包含兩種可能,假如 TM 從全部參與者得到的反饋都是 yes 響應,那麼就會執行事務提交。
1. 發送提交請求:TM 向全部 AP 節點發出 commit 請求
2. 事務提交AP 接收到 Commit 請求後,會正式執行事務提交操做,並在完成提交以後釋放在整個事務執行期間佔用的事務資源
3. 反饋事務提交結果:AP 在完成事務提交以後,向 TM 發送 Ack 消息
4. 完成事務:TM 接收到全部 AP 反饋的 ack 消息後,完成事務
事務回滾:
若是第一個階段中的某一個資源預提交失敗,那麼第二個階段就回滾第一階段已經預提交成功的資源假設任何一個 AP 向 TM 反饋了 NO 的響應,或者在等待超時以後,TM 沒法接收到全部 AP 的反饋響應,那麼就會中斷事務
1. 發送回滾請求:TM 向全部 AP 發出 abort 請求
2. 事務回滾:AP 收到 abort 請求後,會利用在第一階段記錄的 Undo 信息來執行事務回滾操做,並在完成回滾以後釋放在整個事務執行期間佔用的資源
3. 反饋事務回滾結果各 AP 在完成事務回滾以後,向 TM 發送 Ack 消息
4. 中斷事務:TM接收到全部 AP 反饋的 ack 消息後,完成事務中斷。
二階段提交將一個事務的處理過程分爲投票和執行兩個階段. 二階段提交的優勢在於,它充分考慮到了分佈式系統的不可靠因素,而且採用很是簡單的方式(兩階段提交)就把因爲系統不可靠從而致使事務提交失敗的機率降到最小
假如一個事務的提交過程總共須要 30 秒的操做,其中 prepare 階段須要 28 秒(主要是確保事務日誌落地磁盤等各類耗時的 I/O 操做),真正的 commit 階段只須要花費兩秒,那麼 Commit 階段發生錯誤的機率與 Prepare 階段相比,只是它的2/28(<10%),也就是說,若是 Prepare 階段成功了,則 Commit 階段因爲時間很是端,失敗機率小,會大大增長分佈式事務成功的機率。
2pc 協議的優缺點:
1. 原理簡單,實現很方便
2. 每個階段都是同步阻塞,會形成性能損耗。
3. 協調者存在單點問題,若是協調者在第二階段出現故障,那麼其餘參與者會一直處於鎖定狀態。
4. 太過保守,任意一個節點失敗都會致使數據回滾
5. 數據不一致問題: 在階段二中,當協調者向全部的參與者發送 commit 請求後,發生了網絡異常致使協調者在還沒有發完 commit 請求以前崩潰,可能會致使只有部分的參與者接收到 commit 請求,剩下沒收到 commit 請求的參與者將沒法提交事務,也就可能致使數據不一致的問題.
3PC 協議主要用來解決 2PC 的同步阻塞問題的一種優化方案,3pc 分爲 3 個階段分別爲:cancommit、Precommit、doCommit。和 2 階段提交的區別在於:
(1) 在協調者和參與者中引入了超時機制,2pc 只有在協調者擁有超時機制,協調者在必定時間內沒受到參與者的信息則默認爲失敗;
(2) 把 2 階段提交的第一個階段拆分紅了兩個步驟。
cancommit 階段:協調者向參與者發送 commit 請求,參與者若是能夠提交就返回 yes 的響應,不然返回 No 的響應。這一階段主要是肯定分佈式事務的參與者是否具有了完成commit 的條件,並不會執行事務操做。
1. 詢問參與者是否能夠執行事務提交操做。
2. 正常狀況下只要可以順利執行事務,就返回 yes 的響應,並進入預備狀態。
precommit 階段:事務協調者根據參與者的反饋狀況來決定是否繼續執行事務的 precommit 操做,在這一個階段,會有兩種可能性,第一種是,在 cancommit 階段全部參與者都反饋的是 yes,則會進行事務預執行。
1. 協調者向參與者發送 precommit 請求。
2. 參與者收到 precommit 請求後,執行事務操做,並把事務的 undo 和 redo 信息記錄到事務日誌中3. 返回事務的執行結果給到協調者,並等待最終的提交指令若是任意一個事務參與者在第一階段返回了 no,則執行事務中斷請求。
1. 向全部事務參與者發送事務中斷請求。
2. 對於事務參與者來講,不管是收到協調者的中斷請求,仍是等待協調者新的指令以前出現超時,參與者都會中斷事務。
doCommit 階段:這個階段一樣存在兩種狀況,正常狀況下,precommit 都響應了 ack 給到協調者,那麼協調者會發起事務提交請求。
1. 協調者向全部參與者發送 docommit 請求。
2. 參與者收到 docommit 請求後,執行事務提交操做,並釋放全部事務資源。
3. 事務提交之後返回 ack 給到協調者。
4. 協調者收到全部參與者的響應後,完成事務。
若是在 precommit 階段,有參與者沒有發送 ack 給到協調者,那麼則執行事務中斷指令。
1. 協調者向全部參與者發送中斷事務的請求。
2. 參與者收到請求之後,利用在第二個階段記錄的 undo 信息來執行事務回滾操做。
3. 向協調者發送 ack 消息,協調者收到消息之後,執行事務中斷操做。
在 java 中,分佈式事務主要的規範是 JTA/XA . JTA 是 java 的事務管理器規範,JTA 全稱爲 Java Transaction API, JTA 定義了一組統一的事務編程的接口,基於X/OpenDTP 規範設計的分佈式事務編程接口規範。XA 是工業標準的 X/Open DTP規範,基於 JTA 規範的第三方分佈式事務框架有 Jotm 和 Atomikos
JOTM:JOTM (java open transaction manager)是 ObjectWeb 的一個開源 JTA 實現,提供 JTA 分佈式事務的功能可是 JOTM 存在一個問題,在使用中不能自動 rollback,不管什麼狀況都 commit。
Atomikos:與 JOTM 相比,Atomikos 更加穩定,本來 Atomikos 是商業項目,後來開源。論壇比較活躍,有問題能夠隨時解決。Atomikos 與SpringBoot集成參照 http://www.javashuo.com/article/p-dhptkvxl-bn.html。
TCC兩階段補償方案:
TCC是Try-Confirm-Cancel, 好比在支付場景中,先凍結一筆資金,再去發起支付。若是支付成功,則凍結資金進行實際扣除;若是支付失敗,則取消資金凍結
目前互聯網領域裏有幾種流行的分佈式解決方案,但都沒有像以前所說的 XA 事務同樣造成 X/OpenDTP 那樣的工業規範,而是僅僅在具體的行業裏得到較多的承認;你們熟知的CAP 和 BASE 理論,對於 CAP 來講,對於共享數據的系統,因爲網絡分區問題的存在,咱們只能知足 AP 或者 CP;對於 BASE 理論,知足基本可用。因此其實咱們在落地數據一致性解決方案是,基本上都會選擇一個平衡點,也就是酸鹼平衡理論,ACID 是酸、 BASE 是鹼;ACID 是強一致性、BASE 是弱一致性。強一致性表明數據庫自己不會出現不一致,每一個事務是原子的,或者成功或者失敗,事物間是隔離的,互相徹底不影響,並且最終狀態是持久落盤的,
CAP定理是由加州大學伯克利分校Eric Brewer教授提出來的,他指出WEB服務沒法同時知足一下3個屬性:
具體地講在分佈式系統中,在任何數據庫設計中,一個Web應用至多隻能同時支持上面的兩個屬性。顯然,任何橫向擴展策略都要依賴於數據分區。所以,設計人員必須在一致性與可用性之間作出選擇。
所以,數據庫會從一個明確的狀態到另一個明確的狀態.; 而 BASE 體現的是最終一致性,容許出現中間狀態。因此對於對於服務來講,有不少的方案去選擇:
1. 提供查詢服務確認數據狀態、
2.基於消息隊列實現冪等操做對於重發保證數據的安全性
3.補償操做(提供回調機制等操做)
4.按期校對(如銀行的按期對帳系統)
5.既然是基於Base的最終一致性,那麼就是容許出現中間狀態,這裏能夠採用狀態機(其實能夠當成一個狀態字段)的方式去作,經過狀態驅動數據變化(經過狀態去修改數據where操做)。
業務接口整合,避免分佈式事務:
這個方案就是把一個業務流程中須要在一個事務裏執行的多個相關業務接口包裝整合到一個事務中,好比咱們能夠講 A/B/C 整合爲一個服務 D 來實現單一事務的業務流程服務。
什麼是冪等:
簡單來講:重複調用屢次產生的業務結果與調用一次產生的業務結果相同; 在分佈式架構中,咱們調用一個遠程服務去完成一個操做,除了成功和失敗之外,還有未知狀態,那麼針對這個未知狀態,咱們會採起一些重試的行爲; 或者在消息中間件的使用場景中,消費者可能會重複收到消息。對於這兩種狀況,消費端或者服務端須要採起必定的手段,也就是考慮到重發的狀況下保證數據的安全性。通常咱們經常使用的手段
基於消息的最終一致性方案實踐:
在最終一致性這個方案上,也有兩種選擇方案,一種是基於可靠消息中間件來實現異步的最終一致性、另外一種就是經過 MQ 來實現最大努力通知型。這兩種都比較常見,好比對接過支付寶支付的 api,當你調用支付支付成功之後,支付寶會提供一個異步回調,調用配置好的指定的接口地址。在這個接口中,你能夠得到支付寶的支付結果並根據結果作相應的處理。最後必需要返回一個 ack 給到支付寶的回調 api,告訴他這邊已經處理成功了。不然,支付寶的異步回調會不斷重試,固然有重試次數,以及重試的間隔時間。經過異步消息執行方案的本質是,把兩個事務轉化成兩個本地事務,而後依靠消息自己的可靠性,以及消息的重試機制達到最終一致性。