分布式事務的解決方案
分布式事務是什麽:
分布式事務是指事務的參與者、支持事務的服務器、資源服務器以及事務管理器分別位於不同的分布式系統的不同節點之上。
為什麽會產生分布式事務:
當我們的單個數據庫的性能產生瓶頸的時候,我們可能會對數據庫進行分區,這裏所說的分區指的是物理分區,分區之後可能不同的庫就處於不同的服務器上了,這個時候單個數據庫的ACID已經不能適應這種情況了,而在這種ACID的集群環境下,再想保證集群的ACID幾乎是很難達到,或者即使能達到那麽效率和性能會大幅下降,最為關鍵的是再很難擴展新的分區了,這個時候如果再追求集群的ACID會導致我們的系統變得很差。
我們假設有如下一個架構,這是一個簡單的電商架構平臺,兩個應用節點,一個數據庫,一個負載均衡器。這個架構下,每天會產生將近 100W 的訂單量。那麽一個月的數據量就會超過 3000W。而隨著數據量的不斷擴大,對於訂單表的相關查詢操作的性能開銷就越來越大。並且響應耗時也越來越長。這個時候我們需要考慮到數據庫的優化問題。也就是對數據庫進行分表分庫,達到分攤數據庫壓力以及減少數據庫單表數據量的目的。
分庫分表以後帶來的問題:
分庫分表以後,一方面分擔了單庫帶來的性能壓力;另一方面,減少了單表的數據量。完美的解決了我們遇到的性能問題。但是,隨著而來的又有另外的問題。
? 比如有這樣一個場景,訂單支付成功以後需要扣減庫存。在數據庫分庫分表之前,所有數據都在同一個庫裏面,可以通過事務操作就很容易達到數據一致性的目的。但是在數據庫做了拆分後,訂單狀態更新是屬於訂單的數據庫,而庫存扣減是屬於庫存的數據庫。原本單庫的事務操作就變成了多庫的事務操作。但是每個庫的事務只有自己知道,訂單庫並不知道庫存庫的事務執行結果,庫存庫也不知道訂單庫的修改結果。所以就造成了分布式事務的問題。其實也叫分布式數據一致性。
解決方案:
經典的 X/OpenDTP 事務模型:
X/Open DTP(X/Open Distributed Transaction Processing Reference Model) 是X/Open 這個組織定義的一套分布式事務的標準,也就是定義了規範和 API 接口,由各個廠商進行具體的實現。這個標準提出了使用二階段提交(2PC – Two-Phase-Commit)來保證分布式事務的完整性。後來 J2EE 也遵循了 X/OpenDTP 規範,設計並實現了 java 裏的分布式事務編程接口規範-JTA
X/OpenDTP 角色:在 X/OpenDTP 事務模型中,定義了三個
- AP: application, 應用程序,也就是業務層。哪些操作屬於一個事務,就是 AP 定義的。
- RM: Resource Manager,資源管理器。一般是數據庫,也可以是其他資源管理器,比如數據庫,消息隊列,文件系統。
- TM: Transaction Manager ,事務管理器、事務協調者,負責接收來自用戶程序(AP)發起的 XA 事務指令,並調度和協調參與事務的所有 RM(數據庫),確保事務正確完成。
在分布式系統中,每一個機器節點雖然都能夠明確知道自己在進行事務操作過程中的結果是成功還是失敗,但卻無法直接獲取到其他分布式節點的操作結果。因此當一個事務操作需要跨越多個分布式節點的時候,為了保持事務處理的 ACID 特性,就需要引入一個“協調者”(TM)來統一調度所有分布式節點的執行邏輯,這些被調度的分布式節點被稱為 AP。TM 負責調度 AP 的行為,並最終決定這些 AP 是否要把事務真正進行提交到(RM)。
完成事務操作主要有以下幾個步驟:
1. 參與分布式事務的應用程序(AP)先到 TM 上註冊全局事務。
2. 然後各個 AP 直接在相應的資源管理器(RM)上進行事務操作。
3. 操作完成以後,各個 AP 反饋事務的處理結果給到 TM。
4. TM 收到所有 AP 的反饋以後,通過數據庫提供的 XA 接口進行數據提交或者回滾操作。
2pc 提交(two -phaseCommit):
在 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:
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集成參照 https://www.cnblogs.com/wuzhenzhao/p/10315130.html。
互聯網行業的數據一致性問題解決方案:
目前互聯網領域裏有幾種流行的分布式解決方案,但都沒有像之前所說的 XA 事務一樣形成 X/OpenDTP 那樣的工業規範,而是僅僅在具體的行業裏獲得較多的認可;大家熟知的CAP 和 BASE 理論,對於 CAP 來說,對於共享數據的系統,由於網絡分區問題的存在,我們只能滿足 AP 或者 CP;對於 BASE 理論,滿足基本可用。所以其實我們在落地數據一致性解決方案是,基本上都會選擇一個平衡點,也就是酸堿平衡理論,ACID 是酸、 BASE 是堿;ACID 是強一致性、BASE 是弱一致性。強一致性代表數據庫本身不會出現不一致,每個事務是原子的,或者成功或者失敗,事物間是隔離的,互相完全不影響,而且最終狀態是持久落盤的,
CAP定理是由加州大學伯克利分校Eric Brewer教授提出來的,他指出WEB服務無法同時滿足一下3個屬性:
- 一致性(Consistency) : 客戶端知道一系列的操作都會同時發生(生效)
- 可用性(Availability) : 每個操作都必須以可預期的響應結束
- 分區容錯性(Partition tolerance) : 即使出現單個組件無法可用,操作依然可以完成
具體地講在分布式系統中,在任何數據庫設計中,一個Web應用至多只能同時支持上面的兩個屬性。顯然,任何橫向擴展策略都要依賴於數據分區。因此,設計人員必須在一致性與可用性之間做出選擇。
因此,數據庫會從一個明確的狀態到另外一個明確的狀態.; 而 BASE 體現的是最終一致性,允許出現中間狀態。所以對於對於服務來說,有很多的方案去選擇:
1. 提供查詢服務確認數據狀態、
2.基於消息隊列實現冪等操作對於重發保證數據的安全性
3.補償操作(提供回調機制等操作)
4.定期校對(如銀行的定期對賬系統)
5.既然是基於Base的最終一致性,那麽就是允許出現中間狀態,這裏可以采用狀態機(其實可以當成一個狀態字段)的方式去做,通過狀態驅動數據變化(通過狀態去修改數據where操作)。
業務接口整合,避免分布式事務:
這個方案就是把一個業務流程中需要在一個事務裏執行的多個相關業務接口包裝整合到一個事務中,比如我們可以講 A/B/C 整合為一個服務 D 來實現單一事務的業務流程服務。
基於消息的最終一致性方案實踐:
在最終一致性這個方案上,也有兩種選擇方案,一種是基於可靠消息中間件來實現異步的最終一致性、另一種就是通過 MQ 來實現最大努力通知型。這兩種都比較常見,比如對接過支付寶支付的 api,當你調用支付支付成功以後,支付寶會提供一個異步回調,調用配置好的指定的接口地址。在這個接口中,你可以獲得支付寶的支付結果並根據結果做相應的處理。最後必須要返回一個 ack 給到支付寶的回調 api,告訴他這邊已經處理成功了。否則,支付寶的異步回調會不斷重試,當然有重試次數,以及重試的間隔時間。通過異步消息執行方案的本質是,把兩個事務轉化成兩個本地事務,然後依靠消息本身的可靠性,以及消息的重試機制達到最終一致性。
分布式事務的解決方案