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.
- 基于第一階段的投票結(jié)果進(jìn)行決策
- 請(qǐng)求提交階段
- 優(yōu)點(diǎn)
- 利用數(shù)據(jù)庫(kù)自身的功能進(jìn)行本地事務(wù)的提交和回滾,也就是提交和回滾實(shí)際不需要我們實(shí)現(xiàn)。
- 如mysql XA規(guī)范實(shí)現(xiàn)。
- 利用數(shù)據(jù)庫(kù)自身的功能進(jìn)行本地事務(wù)的提交和回滾,也就是提交和回滾實(shí)際不需要我們實(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)。
- 事務(wù)協(xié)調(diào)者掛了,整個(gè)事務(wù)就執(zhí)行不下去了。
- 數(shù)據(jù)不一致問(wèn)題
- 網(wǎng)絡(luò)抖動(dòng),導(dǎo)致某些參與者無(wú)法收到協(xié)調(diào)者的請(qǐng)求,而某些收到了,導(dǎo)致數(shù)據(jù)不一致。
- 同步阻塞
- 提交協(xié)議是阻塞協(xié)議,如果事務(wù)協(xié)調(diào)器宕機(jī),某些參與者將無(wú)法解決他們的事務(wù)問(wèn)題
- 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í)間
- 重做日志(redo log)
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)
- 發(fā)送預(yù)提交請(qǐng)求
- 協(xié)調(diào)者從所有參與者反有一個(gè)No反應(yīng),或者超時(shí)
- 發(fā)送中斷請(qǐng)求
- 協(xié)調(diào)者向所有參與者發(fā)送中斷請(qǐng)求
- 中斷事務(wù)
- 參與者收到中斷請(qǐng)求,執(zhí)行事務(wù)中斷。
- 發(fā)送中斷請(qǐng)求
- 協(xié)調(diào)者從所有參與者反饋都是Yes響應(yīng)
- 提交( 只要預(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ù)。
- 發(fā)送提交請(qǐng)求
- 中斷事務(wù)
- 協(xié)調(diào)者 預(yù)提交 沒(méi)有收到參與者Ack 響應(yīng)(非ack或者超市),執(zhí)行中斷。
- 執(zhí)行提交
- 提交請(qǐng)求階段
- 如何解決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í)行。
- 冪等問(wèn)題,
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ǔ)償。
- 主動(dòng)請(qǐng)求 txManager
2 補(bǔ)償出現(xiàn)
自動(dòng)補(bǔ)償
手動(dòng)補(bǔ)償
-
場(chǎng)景
- 服務(wù)掛了
- 執(zhí)行完事務(wù)掛了
- 沒(méi)有執(zhí)行事務(wù)掛了
- lcn 記錄開始執(zhí)行業(yè)務(wù)之前 事務(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ù)重試,保證最終一致性。
- 如 下單服務(wù)與庫(kù)存服務(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ù)的
如何是特別嚴(yán)格,如資金 那我 tcc 來(lái)保證強(qiáng)一致性。
可以不采用分布式事務(wù), 人工去排除。




