中文字幕日韩精品一区二区免费_精品一区二区三区国产精品无卡在_国精品无码专区一区二区三区_国产αv三级中文在线

終于有人把分布式事務(wù)說清楚了!

前言

余姚網(wǎng)站建設(shè)公司成都創(chuàng)新互聯(lián),余姚網(wǎng)站設(shè)計制作,有大型網(wǎng)站制作公司豐富經(jīng)驗。已為余姚1000多家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\外貿(mào)營銷網(wǎng)站建設(shè)要多少錢,請找那個售后服務(wù)好的余姚做網(wǎng)站的公司定做!

這篇文章將給大家介紹一下對分布式事務(wù)的一些見解,并講解分布式事務(wù)處理框架 TX-LCN 的執(zhí)行原理,錯誤之處望各位不吝指正。

終于有人把分布式事務(wù)說清楚了!

1. 什么情況下需要使用分布式事務(wù)?

使用的場景很多,先舉一個常見的:在微服務(wù)系統(tǒng)中,如果一個業(yè)務(wù)需要使用到不同的微服務(wù),并且不同的微服務(wù)對應(yīng)不同的數(shù)據(jù)庫。

打個比方:電商平臺有一個客戶下訂單的業(yè)務(wù)邏輯,這個業(yè)務(wù)邏輯涉及到兩個微服務(wù),一個是庫存服務(wù)(庫存減一),另一個是訂單服務(wù)(訂單數(shù)加一),示意圖如下:

終于有人把分布式事務(wù)說清楚了!

如果在執(zhí)行這個業(yè)務(wù)邏輯時沒有使用分布式事務(wù),當(dāng)庫存與訂單其中一個出現(xiàn)故障時,就很可能出現(xiàn)這樣的情況:庫存數(shù)據(jù)庫的值減少了 1,但是訂單數(shù)據(jù)庫沒有變化;或是庫存沒變化,多了一個訂單,也就是出現(xiàn)了數(shù)據(jù)不一致現(xiàn)象。

所以在類似的場合下我們要使用分布式事務(wù),保證數(shù)據(jù)的一致性。

2. 分布式事務(wù)的解決思路

2.1引入:MySQL 中的兩階段提交策略

在談分布式事務(wù)的解決思路之前,我們先來看看單一數(shù)據(jù)源是如何做事務(wù)處理的,我們可以從中獲取一些啟發(fā)。

我們以 MySQL 的 InnoDB 引擎為例,由于 MySQL 中有兩套日志機(jī)制,一套是存儲層的 redo log,另一套是 server 層的 binlog,每次更新數(shù)據(jù)都要對兩個日志進(jìn)行更新。為了防止寫日志時只寫了其中一個而沒有寫另外一個,MySQL 使用了一個叫兩階段提交的方式保證事務(wù)的一致性。具體是這樣的:

假設(shè)創(chuàng)建一個這樣的數(shù)據(jù)庫:mysql> create table T(ID int primary key, c int);, 然后執(zhí)行一條這樣的更新語句:mysql> update T set c=c+1 where ID=2;

這條更新語句的執(zhí)行流程是這樣子的:

  1. 首先執(zhí)行器會找引擎取 ID=2 這一行數(shù)據(jù)

  2. 拿到數(shù)據(jù)后會把數(shù)據(jù)進(jìn)行+1 操作,然后調(diào)用引擎接口把新數(shù)據(jù)寫入

  3. 引擎將數(shù)據(jù)更新到內(nèi)存中,并將操作記錄到 redo log 里,此時 redo log 處于 prepare 狀態(tài)。但它不會提交事務(wù),只是通知執(zhí)行器已經(jīng)完成任務(wù),可以隨時提交。

  4. 執(zhí)行器生成這個操作的 binlog,并把 binlog 寫入磁盤

  5. 最后執(zhí)行器調(diào)用引擎的事務(wù)接口,把 redo log 改為提交狀態(tài),更新完成。

在上述過程中,redo log 寫完后沒有直接提交,而是處于 prepare 狀態(tài),等通知執(zhí)行器并把 binlog 寫完后,redo log 再進(jìn)行提交。這個過程就是兩階段提交,這是一個精妙的設(shè)計。

可能你會問為什么要有兩階段提交?如果不采用兩階段提交的話,也就是使用一階段提交,那就相當(dāng)于按順序執(zhí)行寫 redo log 和 binlog,如果寫完 redo log 后系統(tǒng)出現(xiàn)了故障,那么就會只有 redo log 記錄了操作,binlog 沒有記錄,造成數(shù)據(jù)不一致;使用兩階段提交的話,假設(shè)寫完 redo log 后系統(tǒng)出現(xiàn)了故障,由于事務(wù)還沒有提交,所以可以順利回滾。

兩階段提交的設(shè)計還有什么好處?首先要奠定一個概念:一個操作執(zhí)行的時間越長,這個操作就越有可能失敗。打個比方,你吃飯要用 20 分鐘,上廁所要用 1 分鐘,在吃飯的過程中收到微信消息的概率肯定比去上廁所的過程中收到微信消息的概率大。由于在數(shù)據(jù)庫中更新操作的時間要遠(yuǎn)大于提交事務(wù)的時間,所以先把更新操作做完,等所有耗時操作都做完最后再提交事務(wù),能夠最大程度保證事務(wù)執(zhí)行成功。

2.2分布式事務(wù)的兩階段提交策略

根據(jù)上述的兩階段提交策略,分布式事務(wù)也可以采取類似的辦法完成事務(wù)。

在第一階段,我們要新增一個事務(wù)管理者的角色,通過它來協(xié)調(diào)各個數(shù)據(jù)源。還是拿開頭的訂單案例講解,在執(zhí)行下訂單的邏輯時,先讓各個數(shù)據(jù)庫去執(zhí)行各自的事務(wù),比如從庫存中減 1,在訂單庫中加 1,但是完成后不提交,只是通知事務(wù)管理者已經(jīng)完成了任務(wù)。

終于有人把分布式事務(wù)說清楚了!

到了第二階段,由于在階段一我們已經(jīng)收到了各個數(shù)據(jù)源是否就緒的信息,只要有一個數(shù)據(jù)源沒有就緒,在第二階段就通知所有數(shù)據(jù)源回滾;如果全部數(shù)據(jù)源都已經(jīng)就緒,就通知所有數(shù)據(jù)源提交事務(wù)。

終于有人把分布式事務(wù)說清楚了!

總結(jié)一下這個兩階段提交的過程就是:首先事務(wù)管理器通知各個數(shù)據(jù)源進(jìn)行操作,并返回是否準(zhǔn)備好的信息。等所有數(shù)據(jù)源都準(zhǔn)備好后,再統(tǒng)一發(fā)送事務(wù)提交(回滾)的通知讓各個數(shù)據(jù)源提交事務(wù)。由于最后的提交操作耗時極短,所以操作失敗的可能性會很低。

那么這個兩階段提交協(xié)議可能存在什么缺點(diǎn)呢?很可能存在被阻塞的問題,假如其中一個數(shù)據(jù)源出現(xiàn)了某些問題阻塞了,既不能返回成功信息,也不能返回失敗信息,那么整個事務(wù)將被阻塞。對應(yīng)的策略是添加一些倒計時的操作,或者是重新發(fā)送消息。

歡迎大家關(guān)注我的公種浩【程序員追風(fēng)】,文章都會在里面更新,整理的資料也會放在里面。

3. 分布式事務(wù)框架 TX-LCN

講了這么多理論的知識,下面講解一款真正應(yīng)用在生產(chǎn)中的分布式事務(wù)框架 TX-LCN 的運(yùn)行原理。(典型的分布式事務(wù)框架不止 TX-LCN,比如還有阿里的 GTS,不過 GTS 是收費(fèi)的,TX-LCN 是開源的)

我們先看一下官方文檔中給出的運(yùn)行原理示意圖:

終于有人把分布式事務(wù)說清楚了!

思路和我們上面講的兩階段分布式事務(wù)處理流程差不多(有小不同),核心步驟分為 3 步:

  1. 創(chuàng)建事務(wù)組:在事務(wù)發(fā)起方開始執(zhí)行業(yè)務(wù)代碼之前先調(diào)用 TxManager 創(chuàng)建事務(wù)組對象,然后拿到事務(wù)表示 GroupId 的過程。簡單來說就是對這次下訂單的操作在事務(wù)管理中心里創(chuàng)建一個對象,拿到一個 id。

  2. 加入事務(wù)組:參與方在執(zhí)行完業(yè)務(wù)方法后,將該模塊的事務(wù)信息通知給 TxManager 的操作。也就是指各個數(shù)據(jù)源(各個服務(wù))完成操作后,和事務(wù)管理中心說一聲,注冊一下自己。

  3. 通知事務(wù)組:發(fā)起方執(zhí)行業(yè)務(wù)代碼后,將發(fā)起方執(zhí)行結(jié)果狀態(tài)通知給 TxManager,TxManager 將根據(jù)事務(wù)最終狀態(tài)和事務(wù)組的信息來通知相應(yīng)的參與模塊提交或回滾事務(wù),并返回結(jié)果給事務(wù)發(fā)起方。和客戶打交道的下訂單服務(wù)會收到減庫存和加訂單是否成功消息,它會把這兩個消息通知給事務(wù)管理者,事務(wù)管理者根據(jù)情況通知兩個庫存服務(wù)提交事務(wù)或回滾事務(wù)。

目前發(fā)現(xiàn)網(wǎng)上有一篇不錯的 TX-LCN 執(zhí)行源碼分析文章: https://blog.csdn.net/cgj296645438/article/details/93860384

文章中跟著源碼走一遍會發(fā)現(xiàn)和上面的流程圖差不多,落實到代碼中有一些精彩的地方,比如:

public?Object?runTransaction(DTXInfo?dtxInfo,?BusinessCallback?business)?throws?Throwable?{
????????if?(Objects.isNull(DTXLocalContext.cur()))?{
????????????DTXLocalContext.getOrNew();
????????}?else?{
????????????return?business.call();
????????}
????????log.debug("<----?TxLcn?start?---->");
????????DTXLocalContext?dtxLocalContext?=?DTXLocalContext.getOrNew();
????????TxContext?txContext;
????????//?----------?保證每個模塊在一個DTX下只會有一個TxContext?----------?//
????????if?(globalContext.hasTxContext())?{
????????????//?有事務(wù)上下文的獲取父上下文
????????????txContext?=?globalContext.txContext();
????????????dtxLocalContext.setInGroup(true);
????????????log.debug("Unit[{}]?used?parent's?TxContext[{}].",?dtxInfo.getUnitId(),?txContext.getGroupId());
????????}?else?{
????????????//?沒有的開啟本地事務(wù)上下文
????????????txContext?=?globalContext.startTx();
????????}
????????//......
}

這段代碼保證了每個模塊下只會有一個 TxContext,換個說法就是假設(shè)一個業(yè)務(wù)邏輯不是操作不同的數(shù)據(jù)源,而是對同一個數(shù)據(jù)源執(zhí)行多次相同的操作,那么該數(shù)據(jù)源對應(yīng)的模塊在 DTX 下會只有一個 TxContext

終于有人把分布式事務(wù)說清楚了!

LCN 的事務(wù)協(xié)調(diào)機(jī)制

LCN 的口號是:LCN 并不生產(chǎn)事務(wù),LCN 只是本地事務(wù)的協(xié)調(diào)工。大家肯定會有個疑問,它不生產(chǎn)事務(wù),那么它是怎么控制各個模塊在完成事務(wù)的邏輯操作之后不馬上提交,而是等到 TxManager 最后一起通知各模塊提交的呢?

因為每個模塊都是一個 TxClient,每個 TxClient 下都有一個連接池,是框架自定義的連接池,對 Connection 使用靜態(tài)代理的方式進(jìn)行包裝。

public?class?LcnConnectionProxy?implements?Connection?{
????private?Connection?connection;
????public?LcnConnectionProxy(Connection?connection)?{
????????this.connection?=?connection;
????}
????/**
?????*?notify?connection
?????*
?????*?@param?state?transactionState
?????*?@return?RpcResponseState?RpcResponseState
?????*/
????public?RpcResponseState?notify(int?state)?{
????????try?{
????????????if?(state?==?1)?{
????????????????log.debug("commit?transaction?type[lcn]?proxy?connection:{}.",?this);
????????????????connection.commit();
????????????}?else?{
????????????????log.debug("rollback?transaction?type[lcn]?proxy?connection:{}.",?this);
????????????????connection.rollback();
????????????}
????????????connection.close();
????????????log.debug("transaction?type[lcn]?proxy?connection:{}?closed.",?this);
????????????return?RpcResponseState.success;
????????}?catch?(Exception?e)?{
????????????log.error(e.getLocalizedMessage(),?e);
????????????return?RpcResponseState.fail;
????????}
????}
????@Override
????public?void?setAutoCommit(boolean?autoCommit)?throws?SQLException?{
????????connection.setAutoCommit(false);
????}
????//......
}

連接池在沒有接收到通知事務(wù)之前會一直占有著這次分布式事務(wù)的連接資源。等到最后 TxManager 通知 TxClient 時,TxClient 才會去執(zhí)行相應(yīng)的提交或回滾。所以 LCN 的事務(wù)協(xié)調(diào)機(jī)制相當(dāng)于是攔截了一下連接池,控制了連接的事務(wù)提交。

終于有人把分布式事務(wù)說清楚了!

LCN 的事務(wù)補(bǔ)償機(jī)制

由于我們不能保證事務(wù)每次都正常執(zhí)行,如果在執(zhí)行某個業(yè)務(wù)方法時,本應(yīng)該執(zhí)行成功的操作卻因為服務(wù)器掛機(jī)或網(wǎng)絡(luò)抖動等問題導(dǎo)致事務(wù)沒有正常提交,這種場景就需要通過補(bǔ)償來完成事務(wù)。

在這種情況下 TxManager 會做一個標(biāo)示;然后返回給發(fā)起方。告訴他本次事務(wù)有存在沒有通知到的情況,然后 TxClient 再次執(zhí)行該次請求事務(wù)。

最后

歡迎大家一起交流,喜歡文章記得關(guān)注我點(diǎn)贊轉(zhuǎn)發(fā)喲,感謝支持!

本文題目:終于有人把分布式事務(wù)說清楚了!
瀏覽路徑:http://www.rwnh.cn/article40/jgpiho.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供建站公司、面包屑導(dǎo)航、網(wǎng)站制作、搜索引擎優(yōu)化電子商務(wù)、網(wǎng)站收錄

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站網(wǎng)頁設(shè)計
揭阳市| 安图县| 西充县| 富裕县| 芜湖市| 铅山县| 辛集市| 普兰店市| 十堰市| 军事| 衡南县| 东光县| 沐川县| 会同县| 迁西县| 托克托县| 宁河县| 昌宁县| 大宁县| 昭苏县| 乌恰县| 桓仁| 临澧县| 靖安县| 江陵县| 旅游| 海伦市| 隆回县| 定日县| 元谋县| 米易县| 游戏| 蒙阴县| 苍南县| 佳木斯市| 崇州市| 天长市| 合江县| 绥阳县| 兴山县| 中江县|