數據庫事務要知足幾個要求:ACIDjava
Atomic(原子性) 事務必須是原子的工做單元sql
Consistent(一致性) 事務完成時,必須使全部數據都保持一致狀態數據庫
Isolation(隔離性) 併發事務所作的修改必須和其餘事務所作的修改是隔離的編程
Duration(持久性) 事務完成以後,對系統的影響是永久性的網絡
Mysql裏的事務處理過程架構
事物處理完刪除undo文件,更新redo文件併發
拆分出來,每個模塊是獨立的工程,可以縮短開發週期迴歸測試app
在分佈式系統中,每個機器節點雖然都能明確的知道本身執行的事務是成功仍是失敗,可是卻沒法知道其餘分佈式節點的事務執行狀況。所以,當一個事務要跨越多個分佈式節點的時候(好比,下單流程,下單系統和庫存系統可能就是分別部署在不一樣的分佈式節點中),爲了保證該事務能夠知足ACID,就要引入一個協調者(Cooradinator)。其餘的節點被稱爲參與者(Participant)。協調者負責調度參與者的行爲,並最終決定這些參與者是否要把事務進行提交。框架
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接口函數由數據庫廠商提供
AP application :觸發分佈式事物指令 (應用程序)
RM resouces manager 資源管理器。 通常表示數據庫,必須實現XA定義的接口
TM transaction manager 事務管理器,負責協調和事務管理
各個AP節點執行事務操做,將undo和redo信息記錄到事務日誌中,儘可能把提交過程當中所消耗時間的操做和準備都提早完成後確保後續
事務提交的成功率
各個AP成功執行了事務操做,那麼反饋給TM yes的response;若是AP沒有成功執行事務,就反饋TM no的response
假設一個事務的提交過程總共須要30s, 其中prepare操做須要28(事務日誌落地磁盤及各類io操做),而真正commit只須要2s
那麼,commit階段發生錯誤的機率和prepare相比, 2/28 (<10%) .只要第一個階段成功,那麼commit階段出現失敗的機率就很是小
大大增長了分佈式事務的成功機率
階段一:canCommit
詢問,按照全部人的返回決定是否能夠執行操做
階段二:preCommit
預執行,若是超時認爲執行失敗
階段三:doCommit
若是超時了,仍是會真正執行
相對於2PC,3PC主要解決的單點故障問題,並減小阻塞,由於一旦參與者沒法及時收到來自協調者的信息以後,他會默認執行commit。而不會一直持有事務資源並處於阻塞狀態。可是這種機制也會致使數據一致性問題,由於,因爲網絡緣由,協調者發送的abort響應沒有及時被參與者接收到,那麼參與者在等待超時以後執行了commit操做。這樣就和其餘接到abort命令並執行回滾的參與者之間存在數據不一致的狀況。
XA 就是 X/Open DTP 定義的事務管理器與資源管理器的接口規範(即接口函數),XA 接口函數由數據庫廠商提供。
JTA是基於X/Open DTP模型開發的java transaction APi規範
經過2pc的方式去完成分佈式事務,雖然經過這種方式可以達到預期的效果,可是咱們在現實中不多會用到2pc方式的提交的XA事務,有幾個緣由
目前互聯網領域裏有幾種流行的分佈式解決方案,但都沒有像以前所說的XA事務同樣造成X/OpenDTP那樣的工業規範,而是僅僅在具體的行業裏得到較多的承認
這個方案就是把一個業務流程中須要在一個事務裏執行的多個相關業務接口包裝整合到一個事務中,好比咱們能夠講A/B/C整合爲一個服務D來實現單一事務的業務流程服務
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(); }
根據上面的僞代碼的實現方案,可能出現幾種狀況
對於上面幾種狀況,問題都不大。那麼咱們分析下消費端的問題
對於第一個問題,如何保證消息不丟失
如今用的比較廣泛的MQ都具備持久化消息的功能,若是消費者宕機或者消費失敗,均可以執行重試機制
也就是說若是隊列中的消息由於網絡異常致使發送屢次的狀況下,仍然須要保證消息被應用屢次與應用一次產生的效果是同樣的
增長一個message_applied(msg_id)表,用來記錄已經被成功應用的消息。在目標系統執行更新操做以前,先檢測該消息是否已經被消費過,消費完成後經過本地事務控制來更新這個「消費表狀態」,用來避免消息重複消費問題
上面這種方式是很是經典的實現,基本避免了分佈式事務,實現了「最終一致性」。
各大知名的電商平臺和互聯網公司,幾乎都是採用相似的設計思路來實現「最終一致性」的。這種方式適合的業務場景普遍,並且比較可靠。不過這種方式技術實現的難度比較大
任何一個服務操做都提供一個查詢接口,用來向外部輸出操做執行的狀態。服務操做的使用方能夠經過接口得知服務操做執行的狀態,而後根據不一樣狀態作不一樣的處理操做
爲了可以實現查詢,每一個服務操做都須要有惟一的流水號
衰減查詢 5s 10s 20s
有了查詢模式,咱們就可以得知操做所處的具體狀態,若是整個操做處於不正常狀態,咱們須要修正操做中的出現問題的子操做。也許是要從新執行,或者取消已完成的操做。經過修復使得整個分佈式系統達到最終一致。這個過程就是補償模式
根據發起形式又分爲
DTS(Distributed Transaction Service)框架是由支付寶在X/OpenDTP模型的基礎上改進的一個設計,定義了相似2PC的標準兩階段接口,業務系統只須要實現對應的接口就可使用DTS的事務功能。DTS最大的特色是放寬了數據庫的強一致約束,保證了數據的最終一致性。
TRYING(SQL事務中的LOCK):
CONFIRMING(SQL事務中的COMMIT)
CONFIRMING 階段
TRYING階段執行成功並開始執行CONFIRMING階段時,默認 CONFIRMING階段是不會出錯的。即:只要TRYING成功,CONFIRMING必定成功。 CANCELING 階段主要是在業務執行錯誤,須要回滾的狀態下執行的業務取消,預留資源釋放。
以上全部的操做須要知足冪等性,冪等性的實現方式能夠是:
一、經過惟一鍵值作處理,即每次調用的時候傳入惟一鍵值,經過惟一鍵值判斷業務是否被操做,若是已被操做,則再也不重複操做
二、經過狀態機處理,給業務數據設置狀態,經過業務狀態判斷是否須要重複執行
如何更通俗的理解TCC事務模型
支付系統接收到會員的支付請求後,須要扣減會員帳戶餘額、增長會員積分(暫時假設須要同步實現)增長商戶帳戶餘額會員系統、商戶系統、積分系統是獨立的三個子系統,沒法經過傳統的事務方式進行處理。
TRYING階段:咱們須要作的就是會員資金帳戶的資金預留,即:凍結會員帳戶的金額(訂單金額)
CONFIRMING階段:咱們須要作的就是會員積分帳戶增長積分餘額,商戶帳戶增長帳戶餘額
CANCELING階段:若是confirming階段出現問題,該階段須要執行的就是解凍釋放咱們扣減的會員餘額
相信作過支付寶交易接口都知道,咱們通常會在支付寶的回調頁面和接口裏,解密參數,而後調用系統中更新交易狀態相關的服務,將訂單更新爲付款成功。同時,只有當咱們回調頁面中輸出了success字樣或者標識業務處理成功相應狀態碼時,支付寶纔會中止回調請求。不然,支付寶會每間隔一段時間後,再向客戶方發起回調請求,直到輸出成功標識爲止。
其實這就是一個很典型的補償例子,跟一些MQ重試補償機制很相似。