分佈式事物梳理

分佈式事務

數據庫事務要知足幾個要求:ACIDjava

Atomic(原子性) 事務必須是原子的工做單元sql

Consistent(一致性) 事務完成時,必須使全部數據都保持一致狀態數據庫

Isolation(隔離性) 併發事務所作的修改必須和其餘事務所作的修改是隔離的編程

Duration(持久性) 事務完成以後,對系統的影響是永久性的網絡

Mysql裏的事務處理過程架構

  1. 記錄redo(記錄數據修改以前原始記錄)和undo log文件,確保日誌在磁盤上的持久化
  2. 更新數據記錄
  3. 提交事務 ,redo 寫入commit記錄

事物處理完刪除undo文件,更新redo文件併發

分佈式事務

緣由

數據庫分庫分表

img

SOA化

拆分出來,每個模塊是獨立的工程,可以縮短開發週期迴歸測試app

img

​ 在分佈式系統中,每個機器節點雖然都能明確的知道本身執行的事務是成功仍是失敗,可是卻沒法知道其餘分佈式節點的事務執行狀況。所以,當一個事務要跨越多個分佈式節點的時候(好比,下單流程,下單系統和庫存系統可能就是分別部署在不一樣的分佈式節點中),爲了保證該事務能夠知足ACID,就要引入一個協調者(Cooradinator)。其餘的節點被稱爲參與者(Participant)。協調者負責調度參與者的行爲,並最終決定這些參與者是否要把事務進行提交。框架

X/OpenDTP事務模型

X/Open Distributed Transaction Processing Reference Model 異步

X/Open是一個組織機構,定義出的一套分佈式事務標準, 定義了規範的API接口

2PC(two -phase-commit), 用來保證分佈式事務的完整性

J2EE 遵循了X/open DTP規範,設計並實現了java裏面的分佈式事務編程接口規範-JTA

XA是X/Open DTP定義的中間件與數據庫之間的接口規範。 XA接口函數由數據庫廠商提供

X/OpenDTP 角色

image-20190318223350760

AP application :觸發分佈式事物指令 (應用程序)

RM resouces manager 資源管理器。 通常表示數據庫,必須實現XA定義的接口

TM transaction manager 事務管理器,負責協調和事務管理

2PC(two -phase-commit)

  • 提交事務請求(投票)
  • 執行事務請求(提交或中斷)

階段一:提交事務請求(投票)

  1. TM向全部的AP發送事務內容,詢問是否能夠執行事務的提交操做,並等待各個AP的響應
  2. 執行事務

各個AP節點執行事務操做,將undo和redo信息記錄到事務日誌中,儘可能把提交過程當中所消耗時間的操做和準備都提早完成後確保後續

事務提交的成功率

  1. 各個AP向TM反饋事務詢問的響應

各個AP成功執行了事務操做,那麼反饋給TM yes的response;若是AP沒有成功執行事務,就反饋TM no的response

階段二:執行事務提交

詳解

執行提交事務

img

​ 假設一個事務的提交過程總共須要30s, 其中prepare操做須要28(事務日誌落地磁盤及各類io操做),而真正commit只須要2s

​ 那麼,commit階段發生錯誤的機率和prepare相比, 2/28 (<10%) .只要第一個階段成功,那麼commit階段出現失敗的機率就很是小

​ 大大增長了分佈式事務的成功機率

中斷事務提交

img

2pc的數據一致性問題

  • 數據不一致。在二階段提交的階段二中,當協調者向參與者發送commit請求以後,發生了局部網絡異常或者在發送commit請求過程當中協調者發生了故障,這回致使只有一部分參與者接受到了commit請求。而在這部分參與者接到commit請求以後就會執行commit操做。可是其餘部分未接到commit請求的機器則沒法執行事務提交。因而整個分佈式系統便出現了數據部一致性的現象。
  • 同步阻塞問題。執行過程當中,全部參與節點都是事務阻塞型的。當參與者佔有公共資源時,其餘第三方節點訪問公共資源不得不處於阻塞狀態
  • 二階段沒法解決的問題:協調者在發出commit消息以後宕機,而惟一接收到這條消息的參與者同時也宕機了。那麼即便協調者經過選舉協議產生了新的協調者,這條事務的狀態也是不肯定的,沒人知道事務是否被已經提交
  • 單點故障。因爲協調者的重要性,一旦協調者發生故障。參與者會一直阻塞下去

3PC(three phase commit)

階段一:canCommit

​ 詢問,按照全部人的返回決定是否能夠執行操做

階段二:preCommit

​ 預執行,若是超時認爲執行失敗

階段三:doCommit

​ 若是超時了,仍是會真正執行

改進點

  • 增長了超時機制
  • 第二階段,若是協調者超時沒有接受到參與者的反饋,則自動認爲失敗,發送abort命令
  • 第三階段,若是參與者超時沒有接受到協調者的反饋,則自動認爲成功開始提交事務(基於機率)

3pc的問題

​ 相對於2PC,3PC主要解決的單點故障問題,並減小阻塞,由於一旦參與者沒法及時收到來自協調者的信息以後,他會默認執行commit。而不會一直持有事務資源並處於阻塞狀態。可是這種機制也會致使數據一致性問題,由於,因爲網絡緣由,協調者發送的abort響應沒有及時被參與者接收到,那麼參與者在等待超時以後執行了commit操做。這樣就和其餘接到abort命令並執行回滾的參與者之間存在數據不一致的狀況。

XA/JTA

​ XA 就是 X/Open DTP 定義的事務管理器與資源管理器的接口規範(即接口函數),XA 接口函數由數據庫廠商提供。

​ JTA是基於X/Open DTP模型開發的java transaction APi規範

概述

​ 經過2pc的方式去完成分佈式事務,雖然經過這種方式可以達到預期的效果,可是咱們在現實中不多會用到2pc方式的提交的XA事務,有幾個緣由

  1. 互聯網電商應用的快速發展,對事務和數據的絕對一致性要求並無傳統企業應用那麼高
  2. XA事務的介入增長了TM中間件,使得系統複雜化
  3. XA事務的性能不高,由於TM要等待RM迴應,因此爲了確保事務儘可能成功提交,等待超時的時間一般比較長,好比30s到幾分鐘,若是RM出現故障或者響應比較慢,則整個事務的性能嚴重降低

互聯網的分佈式事務解決方案

​ 目前互聯網領域裏有幾種流行的分佈式解決方案,但都沒有像以前所說的XA事務同樣造成X/OpenDTP那樣的工業規範,而是僅僅在具體的行業裏得到較多的承認

業務接口整合,避免分佈式事務

​ 這個方案就是把一個業務流程中須要在一個事務裏執行的多個相關業務接口包裝整合到一個事務中,好比咱們能夠講A/B/C整合爲一個服務D來實現單一事務的業務流程服務

最終一致性方案之ebay模式

​ eBay在2008年公佈了一個關於BASE準則提到一個分佈式事務解決方案。eBay的方案實際上是一個最終一致性方案,它主要採用消息隊列來輔助實現事務控制流程,方案的核心是將須要分佈式處理的任務經過消息隊列的方式來異步執行,若是事務失敗,則能夠發起人工重試的糾正流程。人工重試被更多的應用於支付場景,經過對帳系統對過後問題進行處理

​ 好比一個很常見的場景:某個用戶產生了一筆交易,那麼須要在交易表中增長記錄,同時須要修改用戶表的金額(餘額),因爲這兩個表屬於不一樣的遠程服務,因此就會涉及到分佈式事務與數據一致性的問題

user(id, name, amt_sold, amt_bought)
transaction(xid, seller_id, buyer_id, amount)
begin;
INSERT INTO transaction VALUES(xid, $seller_id, $buyer_id, $amount); UPDATE user SET amt_sold = amt_sold + $amount WHERE id = $seller_id; <br />UPDATE user SET amt_bought = amt_bought + $amount WHERE id = $buyer_id;
commit;

那麼在這裏可使用消息隊列(MQ)來作

先啓動一個事務,更新交易表(transaction)後,並不直接更新user表,而是將要對user表進行的更新插入到消息隊列中。

目標系統收到該消息之後,啓動本地事務去對用戶表的餘額作調整

僞代碼

bool result=dao.update();

if(result){

  mq.send();

}

根據上面的僞代碼的實現方案,可能出現幾種狀況

  1. 數據庫操做成功,向MQ中投遞消息也成功
  2. 操做數據庫失敗,不會向MQ中投遞消息
  3. 操做數據庫成功,可是向MQ中投遞消息時失敗,向外拋出異常。數據庫操做回滾

對於上面幾種狀況,問題都不大。那麼咱們分析下消費端的問題

  1. 消息出隊列之後,消費者對應的業務操做要執行成功。若是執行失敗,消息不能失效或者丟失。須要保證消息和業務操做一致
  2. 儘可能避免消息重複消費,若是重複消費,也不能影響業務的執行結果

對於第一個問題,如何保證消息不丟失

如今用的比較廣泛的MQ都具備持久化消息的功能,若是消費者宕機或者消費失敗,均可以執行重試機制

對於如何避免消息的重複消費

  1. 保證消費者的冪等性

    ​ 也就是說若是隊列中的消息由於網絡異常致使發送屢次的狀況下,仍然須要保證消息被應用屢次與應用一次產生的效果是同樣的

  2. 經過消費日誌表來記錄消費狀態

    ​ 增長一個message_applied(msg_id)表,用來記錄已經被成功應用的消息。在目標系統執行更新操做以前,先檢測該消息是否已經被消費過,消費完成後經過本地事務控制來更新這個「消費表狀態」,用來避免消息重複消費問題

上面這種方式是很是經典的實現,基本避免了分佈式事務,實現了「最終一致性」。

​ 各大知名的電商平臺和互聯網公司,幾乎都是採用相似的設計思路來實現「最終一致性」的。這種方式適合的業務場景普遍,並且比較可靠。不過這種方式技術實現的難度比較大

保證最終一致性的模式

查詢模式

​ 任何一個服務操做都提供一個查詢接口,用來向外部輸出操做執行的狀態。服務操做的使用方能夠經過接口得知服務操做執行的狀態,而後根據不一樣狀態作不一樣的處理操做

爲了可以實現查詢,每一個服務操做都須要有惟一的流水號

衰減查詢 5s 10s 20s

補償模式

​ 有了查詢模式,咱們就可以得知操做所處的具體狀態,若是整個操做處於不正常狀態,咱們須要修正操做中的出現問題的子操做。也許是要從新執行,或者取消已完成的操做。經過修復使得整個分佈式系統達到最終一致。這個過程就是補償模式

根據發起形式又分爲

  • 自動恢復:經過對發生失敗操做的接口自動重試或者回滾已經完成的操做
  • 通知運營:若是程序沒法自動完成恢復,則經過運營人員手動進行補償
  • 通知技術:經過監控或者告警通知到技術人員,經過技術手段進行修復

X/OpenDTP模型的支付寶的DTS架構

DTS(Distributed Transaction Service)框架是由支付寶在X/OpenDTP模型的基礎上改進的一個設計,定義了相似2PC的標準兩階段接口,業務系統只須要實現對應的接口就可使用DTS的事務功能。DTS最大的特色是放寬了數據庫的強一致約束,保證了數據的最終一致性。

image-20190321211256865

TCC分爲三個階段

  • TRYING(SQL事務中的LOCK):

    • 階段主要是對業務系統作檢測及資源預留
  • CONFIRMING(SQL事務中的COMMIT)

    • 主要是對業務系統作確認提交
  • CANCELING(SQL事務中的ROLLBACKTRYING)

​ CONFIRMING 階段

​ TRYING階段執行成功並開始執行CONFIRMING階段時,默認 CONFIRMING階段是不會出錯的。即:只要TRYING成功,CONFIRMING必定成功。 CANCELING 階段主要是在業務執行錯誤,須要回滾的狀態下執行的業務取消,預留資源釋放。

​ 以上全部的操做須要知足冪等性,冪等性的實現方式能夠是:

​ 一、經過惟一鍵值作處理,即每次調用的時候傳入惟一鍵值,經過惟一鍵值判斷業務是否被操做,若是已被操做,則再也不重複操做

​ 二、經過狀態機處理,給業務數據設置狀態,經過業務狀態判斷是否須要重複執行

如何更通俗的理解TCC事務模型

​ 支付系統接收到會員的支付請求後,須要扣減會員帳戶餘額、增長會員積分(暫時假設須要同步實現)增長商戶帳戶餘額會員系統、商戶系統、積分系統是獨立的三個子系統,沒法經過傳統的事務方式進行處理。

​ TRYING階段:咱們須要作的就是會員資金帳戶的資金預留,即:凍結會員帳戶的金額(訂單金額)

​ CONFIRMING階段:咱們須要作的就是會員積分帳戶增長積分餘額,商戶帳戶增長帳戶餘額

​ CANCELING階段:若是confirming階段出現問題,該階段須要執行的就是解凍釋放咱們扣減的會員餘額

開源的tcc框架

  • tcc-transaction
  • bytetcc

最大努力通知型

​ 相信作過支付寶交易接口都知道,咱們通常會在支付寶的回調頁面和接口裏,解密參數,而後調用系統中更新交易狀態相關的服務,將訂單更新爲付款成功。同時,只有當咱們回調頁面中輸出了success字樣或者標識業務處理成功相應狀態碼時,支付寶纔會中止回調請求。不然,支付寶會每間隔一段時間後,再向客戶方發起回調請求,直到輸出成功標識爲止。

其實這就是一個很典型的補償例子,跟一些MQ重試補償機制很相似。

相關文章
相關標籤/搜索