分布式事務(wù)

1 2PC

  • 兩階段提交協(xié)議。它引入了一個(gè)事務(wù)協(xié)調(diào)者角色,來(lái)管理各個(gè)參與者
    • 請(qǐng)求提交階段
      • 協(xié)調(diào)器向所有參與者發(fā)送事務(wù)請(qǐng)求,詢問(wèn)是否可以執(zhí)行事務(wù),然后各個(gè)參與者響應(yīng)Yes或者No
    • 提交階段
      • 基于第一階段的投票結(jié)果進(jìn)行決策
        • 當(dāng)所有參與者同意提交,協(xié)調(diào)者才會(huì)通知各個(gè)參與者提交事務(wù)。
        • 否則協(xié)調(diào)者通知各個(gè)參與者取消事務(wù)。
      • 參與者接收到協(xié)調(diào)者發(fā)來(lái)的消息執(zhí)行 本地commit或者 Rollback.
  • 優(yōu)點(diǎn)
    • 利用數(shù)據(jù)庫(kù)自身的功能進(jìn)行本地事務(wù)的提交和回滾,也就是提交和回滾實(shí)際不需要我們實(shí)現(xiàn)。
      • 如mysql XA規(guī)范實(shí)現(xiàn)。
  • 不足
    • 提交協(xié)議是阻塞協(xié)議,如果事務(wù)協(xié)調(diào)器宕機(jī),某些參與者將無(wú)法解決他們的事務(wù)問(wèn)題
      • 同步阻塞
        • 基于數(shù)據(jù)庫(kù)實(shí)現(xiàn)會(huì)鎖住資源。
        • 不基于數(shù)據(jù)庫(kù)實(shí)現(xiàn),也可能要持?jǐn)?shù)據(jù)庫(kù)資源。
      • 單點(diǎn)故障
        • 事務(wù)協(xié)調(diào)者掛了,整個(gè)事務(wù)就執(zhí)行不下去了。
          • 如 參與者發(fā)生完準(zhǔn)備命令之后掛了,每個(gè)本地資源都會(huì)處于鎖定狀態(tài)。
      • 數(shù)據(jù)不一致問(wèn)題
        • 網(wǎng)絡(luò)抖動(dòng),導(dǎo)致某些參與者無(wú)法收到協(xié)調(diào)者的請(qǐng)求,而某些收到了,導(dǎo)致數(shù)據(jù)不一致。
  • XA 規(guī)范
    • 重做日志(redo log)
      • 每當(dāng)有操作執(zhí)行前,在數(shù)據(jù)真正更改前,會(huì)先把相關(guān)操作寫入 redo 日志
    • 回滾日志(undo log)
      • 記錄事務(wù)開始前數(shù)據(jù)的狀態(tài)
    • 二進(jìn)制日志(binlog)
      • 記錄了所有的 DDL 和 DML 語(yǔ)句,除了數(shù)據(jù)查詢語(yǔ)句 select、show 等,還包含語(yǔ)句所執(zhí)行的消耗時(shí)間

2 3PC

  • 過(guò)程
    • 提交請(qǐng)求階段
      • 協(xié)調(diào)器向所有參與者發(fā)送事務(wù)請(qǐng)求,詢問(wèn)是否可以CanCommit,然后各個(gè)參與者響應(yīng)Yes或者No
    • 預(yù)提交
      • 協(xié)調(diào)者從所有參與者反饋都是Yes響應(yīng)
        • 發(fā)送預(yù)提交請(qǐng)求
          • 向參與者發(fā)送預(yù)提交請(qǐng)求
        • 事務(wù)預(yù)提交
          • 參與者接收請(qǐng)求,會(huì)執(zhí)行事務(wù)操作。
        • 響應(yīng)反饋
          • 參與者成功執(zhí)行了事務(wù)操作,返回Ack響應(yīng)
      • 協(xié)調(diào)者從所有參與者反有一個(gè)No反應(yīng),或者超時(shí)
        • 發(fā)送中斷請(qǐng)求
          • 協(xié)調(diào)者向所有參與者發(fā)送中斷請(qǐng)求
        • 中斷事務(wù)
          • 參與者收到中斷請(qǐng)求,執(zhí)行事務(wù)中斷。
    • 提交( 只要預(yù)提交成功, 則一定要保證 真實(shí)提交成功,即使協(xié)調(diào)器下一階段不可用,一般是通過(guò)重試補(bǔ)償?shù)牟呗裕﹥煞N情況
      • 執(zhí)行提交
        • 發(fā)送提交請(qǐng)求
          • 所有參與者返回Ack響應(yīng)后,進(jìn)入提交階段,協(xié)調(diào)者向所有參與者發(fā)送提交請(qǐng)求。
          • 超時(shí)提交 如果參與者 超時(shí)沒(méi)有收到提交請(qǐng)求,也進(jìn)行提交。
        • 事務(wù)提交
          • 參與者接受到提交請(qǐng)求后,執(zhí)行真正事務(wù)提交,完成事務(wù)釋放資源。
        • 響應(yīng)反饋
          • 事務(wù)提交后,參與者向協(xié)調(diào)者發(fā)送Ack響應(yīng)。
        • 完成事務(wù)
          • 協(xié)調(diào)者接受到所有參與者Ack響應(yīng)完成事務(wù)。
      • 中斷事務(wù)
        • 協(xié)調(diào)者 預(yù)提交 沒(méi)有收到參與者Ack 響應(yīng)(非ack或者超市),執(zhí)行中斷。
  • 如何解決2pc同步阻塞問(wèn)題
    • 協(xié)調(diào)者和參與者都引入了超時(shí)機(jī)制
  • 對(duì)2pc 改進(jìn)
    • 引入超時(shí)機(jī)制
    • 添加預(yù)階段,保證最后提交階段參與者節(jié)點(diǎn)狀態(tài)的一致性。

3 TCC

  • TCC 就是一種業(yè)務(wù)層面或者是應(yīng)用層的兩階段提交
  • TCC 分為指代 Try、Confirm、Cancel 分為兩個(gè)階段
    • 第一階段 try 完成業(yè)務(wù)檢查(一致性)、預(yù)留業(yè)務(wù)資源(準(zhǔn)隔離性)。
    • 第二階段
      • Confirm 不做任何業(yè)務(wù)檢查,僅僅使用預(yù)留的資源執(zhí)行業(yè)務(wù)操作,如果失敗會(huì)一直重試
      • Cancel 取消執(zhí)行業(yè)務(wù)操作,釋放預(yù)留的資源,如果失敗會(huì)一直重試
  • 一個(gè)事務(wù)的所有服務(wù)都需要提供這三個(gè)方法 可以根據(jù)表字段去設(shè)計(jì)
//嘗試方法
function try(){
    //記錄日志
    todo save A 轉(zhuǎn)出了 100 元 
    todo save B 轉(zhuǎn)入了 100 元 
    //執(zhí)行轉(zhuǎn)賬
    update amount set balacne = balacne-100 where id = 1
    update amount set balacne = balacne+100 where id = 2
}
//確認(rèn)方法
function confirm(){
    //清理日志
    clean save A 轉(zhuǎn)出了 100 元 
    clean save B 轉(zhuǎn)出了 100 元 
}

//取消方法
function cancle(){
    //加載日志
    load log A
    load log B

     //退錢
    update amount set balacne = balacne+100 where id = 1
    update amount set balacne = balacne-100 where id = 2    
}

  • 場(chǎng)景
    • 分布式事務(wù)要求高,跟錢有關(guān)。
    • 每天系統(tǒng)執(zhí)行時(shí)間比較短。
  • 特點(diǎn)
    • 對(duì)代碼的嵌入性高,要求每個(gè)業(yè)務(wù)需要寫三種步驟的操作
    • 對(duì)有無(wú)本地事務(wù)控制都可以支持使用面廣
    • 數(shù)據(jù)一致性控制幾乎完全由開發(fā)者控制,對(duì)業(yè)務(wù)開發(fā)難度要求高
  • 注意點(diǎn)
    • 冪等問(wèn)題,
      • 網(wǎng)絡(luò)超時(shí),會(huì)重復(fù)調(diào)用參與方的 confirm/cancel 方法
      • 異常事務(wù)的補(bǔ)償執(zhí)行 try 的重復(fù)執(zhí)行。
    • 空回滾問(wèn)題
      • try方法由于網(wǎng)絡(luò)問(wèn)題超時(shí)一直沒(méi)有返回,事務(wù)管理器會(huì)發(fā)出Cancel命令 執(zhí)行了cancel方法。
      • 解決:事務(wù)表 try方法成功執(zhí)行后,會(huì)插入一條記錄,標(biāo)記分支事務(wù) init 狀態(tài) 表示 try執(zhí)行成功。
        • cancel時(shí) 查詢狀態(tài)。
    • 懸掛問(wèn)題
      • try方法由于網(wǎng)絡(luò)問(wèn)題超時(shí),導(dǎo)致cancel先執(zhí)行,然后再執(zhí)行try方法。
      • 解決:空回滾 插入一條條記錄,標(biāo)記分支事務(wù) rollbacked 狀態(tài)。
        • try 開始執(zhí)行的時(shí)候 首先嘗試插入狀態(tài)為 init 的分支事務(wù)
        • 如果失敗表示當(dāng)前分支事務(wù)的記錄已經(jīng)存在,try無(wú)需執(zhí)行。

4 lcn 選取

  • 支持 TCC,TXC,LCN 三種模式
  • 協(xié)調(diào)控制流程
    • image.png
    • 三個(gè)角色, 發(fā)起方,參與方,txManager
      • 發(fā)起方 調(diào)用 txManager 創(chuàng)建事務(wù)組
      • 發(fā)起方 假如執(zhí)行自己業(yè)務(wù), 再調(diào)用參與方A,參與方A 執(zhí)行自己事務(wù),加入到事務(wù)組
        • 參與方A 出現(xiàn)異常, 不需要加入事務(wù)組,發(fā)起方捕獲異常,通知事務(wù)回滾。
      • 發(fā)起方 知道 是提交事務(wù)還是回滾事務(wù)。 通知 txManager
      • txManager 通知每個(gè)事務(wù)參與者,并且每個(gè)參與者都響應(yīng)給了txManager
      • txManager 響應(yīng)給 發(fā)起方。

4.1 TXC 逆向sql

  • 第一階段 執(zhí)行sql的時(shí)候, 攔截并解析,查詢出要受影響的數(shù)據(jù)。記錄下來(lái)

  • 第二階段

    • 提交 將記錄的數(shù)據(jù)刪除
    • 回滾 通過(guò) 記錄的sql和影響的數(shù)據(jù) 創(chuàng)建逆向sql 并執(zhí)行。
  • 流程
    image.png
  • 特點(diǎn)

    • 代碼的嵌入性低
    • 限于對(duì)支持SQL方式的模塊支持
    • 不會(huì)占用數(shù)據(jù)庫(kù)的連接資源,但中間狀態(tài)可見(jiàn)

4.2 Lcn模式

  • 流程
    image.png
  • 特點(diǎn)

    • 對(duì)代碼的嵌入性為低。
    • 僅限于本地存在連接對(duì)象且可通過(guò)連接對(duì)象控制事務(wù)的模塊
    • 事務(wù)提交與回滾是由本地事務(wù)方控制,對(duì)于數(shù)據(jù)一致性上有較高的保障
    • 缺陷在于代理的連接需要隨事務(wù)發(fā)起方一共釋放連接,增加了連接占用的時(shí)間, 會(huì)鎖住資源。
  • 負(fù)載問(wèn)題

    • 如 A 服務(wù)分別掉B服務(wù)集群 one tow 方法。 當(dāng)負(fù)載到不同的 實(shí)例 導(dǎo)致出現(xiàn)兩個(gè)事務(wù), 下面代碼 行鎖 鎖住資源導(dǎo)致 資源占用 導(dǎo)致窒息失敗而回滾事務(wù)。
void one(id){
   execute => update demo set state = 1 where id = {id} ;
}

void two(id){
   execute => update demo set state = 2 where id = {id} ;
}

4.3 lcn 對(duì)一些問(wèn)題的解決。

1 超時(shí)機(jī)制
  • 參與者 執(zhí)行完業(yè)務(wù),啟動(dòng)定時(shí)任務(wù),到了時(shí)間 txManager 沒(méi)有通知參與者
    • 主動(dòng)請(qǐng)求 txManager
      • 成功 按響應(yīng)結(jié)果 來(lái)回滾或者提交。
      • 失敗 事務(wù)補(bǔ)償。
2 補(bǔ)償出現(xiàn)
  • 自動(dòng)補(bǔ)償

  • 手動(dòng)補(bǔ)償

  • 場(chǎng)景

    • 服務(wù)掛了
      • 執(zhí)行完事務(wù)掛了
      • 沒(méi)有執(zhí)行事務(wù)掛了
      • lcn 記錄開始執(zhí)行業(yè)務(wù)之前 事務(wù)信息。
  • 記錄發(fā)起方信息。

5 基于消息補(bǔ)償?shù)淖罱K一致性

  • 基于消息隊(duì)列的最終一致性就是一種異步事務(wù)機(jī)制,在業(yè)務(wù)中廣泛應(yīng)用
  • 基于消息補(bǔ)償?shù)囊恢滦灾饕斜镜叵⒈砗偷谌娇煽肯㈥?duì)列等
    • 本地消息表
      • 將分布式事務(wù)拆分成本地事務(wù)進(jìn)行處理,通過(guò)消息日志的方式來(lái)異步執(zhí)行
      • 本地消息表是一種業(yè)務(wù)耦合的設(shè)計(jì)
        • 消息生產(chǎn)方需要額外建一個(gè)事務(wù)消息表,并記錄消息發(fā)送狀態(tài)
        • 消息消費(fèi)方需要處理這個(gè)消息,并完成自己的業(yè)務(wù)邏輯
        • 有一個(gè)異步機(jī)制來(lái)定期掃描未完成的消息,確保最終一致性
        • 兩個(gè)系統(tǒng)通知 可以用zookeeper, 或者接口。
  • image.png
    • 如 下單服務(wù)與庫(kù)存服務(wù)
      • 系統(tǒng)收到下單請(qǐng)求,訂單業(yè)務(wù)存儲(chǔ)訂單庫(kù),并同時(shí)存儲(chǔ)該訂單對(duì)應(yīng)的消息數(shù)據(jù),放入同一個(gè)事務(wù)處理
      • 庫(kù)存服務(wù) 通過(guò)消息中間件收到庫(kù)存更新消息,調(diào)整庫(kù)存業(yè)務(wù),同時(shí)返回業(yè)務(wù)處理結(jié)果。
      • 訂單服務(wù)收到處理結(jié)果后,將本地消息表的數(shù)據(jù)刪除或者設(shè)置為完成。
      • 設(shè)置異步任務(wù),定時(shí)掃描本地消息表,發(fā)現(xiàn)有未完成的任務(wù)重試,保證最終一致性。
  • 缺點(diǎn)
    • 依賴 數(shù)據(jù)庫(kù)消息表。所以并發(fā)不能太大。

6 可靠消息最終一致性。

  • image.png

7 最大努力通知方案

  • 系統(tǒng) A 本地事務(wù)執(zhí)行完之后,發(fā)送個(gè)消息到 MQ;
  • 這里會(huì)有個(gè)專門消費(fèi) MQ 的最大努力通知服務(wù),這個(gè)服務(wù)會(huì)消費(fèi) MQ 然后寫入數(shù)據(jù)庫(kù)中記錄下來(lái),或者是放入個(gè)內(nèi)存隊(duì)列也可以,接著調(diào)用系統(tǒng) B 的接口;
  • 是系統(tǒng) B 執(zhí)行成功就 ok 了;要是系統(tǒng) B 執(zhí)行失敗了,那么最大努力通知服務(wù)就定時(shí)嘗試重新調(diào)用系統(tǒng) B,反復(fù) N 次,最后還是不行就放棄。

8 你們公司是如何處理分布式事務(wù)的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容