RocketMQ 分布式事務(wù)消息

一受神、什么是事務(wù)

事務(wù)是將一次執(zhí)行過程中所涉及的所有操作納入到一個不可分割的執(zhí)行單元,組成事務(wù)的所有操作只有在所有操作均能正常執(zhí)行的情況下才能提交格侯,只要其中任一操作執(zhí)行失敗弛针,都將導(dǎo)致整個事務(wù)的回滾履磨。一句話來說耕皮,就是保證多個操作要么都做仑濒,要么都不做。同時一旦事務(wù)提交朝墩,則其所做的修改會永久保存到數(shù)據(jù)庫醉拓。

二、事務(wù)的四個特性(ACID)

  • A:原子性(Atomicity)
    一個事務(wù)(transaction)中的所有操作收苏,要么全部完成亿卤,要么全部不完成,不會結(jié)束在中間某個環(huán)節(jié)倒戏。事務(wù)在執(zhí)行過程中發(fā)生錯誤怠噪,會被回滾(Rollback)到事務(wù)開始前的狀態(tài),就像這個事務(wù)從來沒有執(zhí)行過一樣杜跷。
  • C:一致性(Consistency)
    事務(wù)的一致性指的是在一個事務(wù)執(zhí)行之前和執(zhí)行之后數(shù)據(jù)庫都必須處于一致性狀態(tài)。如果事務(wù)成功地完成矫夷,那么系統(tǒng)中所有變化將正確地應(yīng)用葛闷,系統(tǒng)處于有效狀態(tài)。如果在事務(wù)中出現(xiàn)錯誤双藕,那么系統(tǒng)中的所有變化將自動地回滾淑趾,系統(tǒng)返回到原始狀態(tài)。
  • I:隔離性(Isolation)
    指的是在并發(fā)環(huán)境中忧陪,當(dāng)不同的事務(wù)同時操縱相同的數(shù)據(jù)時扣泊,每個事務(wù)都有各自的完整數(shù)據(jù)空間近范。由并發(fā)事務(wù)所做的修改必須與任何其他并發(fā)事務(wù)所做的修改隔離。事務(wù)查看數(shù)據(jù)更新時延蟹,數(shù)據(jù)所處的狀態(tài)要么是另一事務(wù)修改它之前的狀態(tài)评矩,要么是另一事務(wù)修改它之后的狀態(tài),事務(wù)不會查看到中間狀態(tài)的數(shù)據(jù)阱飘。
  • D:持久性(Durability)
    指的是只要事務(wù)成功結(jié)束斥杜,它對數(shù)據(jù)庫所做的更新就必須永久保存下來。即使發(fā)生系統(tǒng)崩潰沥匈,重新啟動數(shù)據(jù)庫系統(tǒng)后蔗喂,數(shù)據(jù)庫還能恢復(fù)到事務(wù)成功結(jié)束時的狀態(tài)。

三高帖、InnoDB 事務(wù)實(shí)現(xiàn)

基于衡量事務(wù)的四個特性缰儿,InnoDB 實(shí)現(xiàn)事務(wù)實(shí)際上就是 4 個特性的實(shí)現(xiàn)。

  • 原子性

    • 在 MySQL 中有很多類型的日志散址,二進(jìn)制日志乖阵、查詢?nèi)罩尽㈠e誤日志爪飘、慢查詢?nèi)罩镜鹊纫迤稹3诉@些日志,還提供了兩種事務(wù)日志师崎,redo log 用來保證持久性默终, undo log 是原子性和隔離性實(shí)現(xiàn)的基礎(chǔ)。
    • 數(shù)據(jù)庫每執(zhí)行一條更新數(shù)據(jù)的 sql 就會生成一條 undo log犁罩,比如 insert 一條數(shù)據(jù)齐蔽,就會生出一條 delete 的 undo log。如果事務(wù)執(zhí)行失敗或者調(diào)用 rollback 就可以根據(jù) undo log 做數(shù)據(jù)回滾床估。
  • 隔離性

    • 隔離性是指含滴,事務(wù)內(nèi)部的操作與其他事務(wù)是隔離的,并發(fā)執(zhí)行的各個事務(wù)之間不能互相干擾丐巫。嚴(yán)格的隔離性谈况,對應(yīng)了事務(wù)隔離級別中的Serializable (可串行化),但實(shí)際應(yīng)用中出于性能方面的考慮很少會使用可串行化递胧。
    • InnoDB 采用可重復(fù)讀隔離級別碑韵,使用 MVCC 和行鎖、間隙鎖實(shí)現(xiàn)隔離性缎脾。
  • 持久性

    • InnoDB作為MySQL的存儲引擎祝闻,數(shù)據(jù)是存放在磁盤中的,但如果每次讀寫數(shù)據(jù)都需要磁盤IO遗菠,效率會很低联喘。為此华蜒,InnoDB提供了緩存(Buffer Pool),Buffer Pool中包含了磁盤中部分?jǐn)?shù)據(jù)頁的映射豁遭,作為訪問數(shù)據(jù)庫的緩沖:當(dāng)從數(shù)據(jù)庫讀取數(shù)據(jù)時叭喜,會首先從Buffer Pool中讀取,如果Buffer Pool中沒有堤框,則從磁盤讀取后放入Buffer Pool域滥;當(dāng)向數(shù)據(jù)庫寫入數(shù)據(jù)時,會首先寫入Buffer Pool蜈抓,Buffer Pool中修改的數(shù)據(jù)會定期刷新到磁盤中(這一過程稱為刷臟)启绰。
    • Buffer Pool的使用大大提高了讀寫數(shù)據(jù)的效率,但是也帶了新的問題:如果MySQL宕機(jī)沟使,而此時Buffer Pool中修改的數(shù)據(jù)還沒有刷新到磁盤委可,就會導(dǎo)致數(shù)據(jù)的丟失,事務(wù)的持久性無法保證腊嗡。
    • 于是着倾,redo log被引入來解決這個問題:當(dāng)數(shù)據(jù)修改時,除了修改Buffer Pool中的數(shù)據(jù)燕少,還會在redo log記錄這次操作卡者;當(dāng)事務(wù)提交時,會調(diào)用fsync接口對redo log進(jìn)行刷盤客们。如果MySQL宕機(jī)崇决,重啟時可以讀取redo log中的數(shù)據(jù),對數(shù)據(jù)庫進(jìn)行恢復(fù)底挫。redo log采用的是WAL(Write-ahead logging恒傻,預(yù)寫式日志),所有修改先寫入日志建邓,再更新到Buffer Pool盈厘,保證了數(shù)據(jù)不會因MySQL宕機(jī)而丟失,從而滿足了持久性要求官边。

    既然redo log也需要在事務(wù)提交時將日志寫入磁盤沸手,為什么它比直接將Buffer Pool中修改的數(shù)據(jù)寫入磁盤(即刷臟)要快呢?主要有以下兩方面的原因:
    (1)刷臟是隨機(jī)IO注簿,因?yàn)槊看涡薷牡臄?shù)據(jù)位置隨機(jī)罐氨,但寫redo log是追加操作,屬于順序IO滩援。
    (2)刷臟是以數(shù)據(jù)頁(Page)為單位的,MySQL默認(rèn)頁大小是16KB塔嬉,一個Page上一個小修改都要整頁寫入玩徊;而redo log中只包含真正需要寫入的部分租悄,無效IO大大減少。

  • 一致性

    • 一致性是指事務(wù)執(zhí)行結(jié)束后恩袱,數(shù)據(jù)庫的完整性約束沒有被破壞泣棋,事務(wù)執(zhí)行的前后都是合法的數(shù)據(jù)狀態(tài)。
    • 一致性不僅由數(shù)據(jù)庫本身來保證畔塔,同時業(yè)務(wù)系統(tǒng)也保證數(shù)據(jù)的一致性潭辈。

四、分布式事務(wù)的由來

現(xiàn)代軟件架構(gòu)隨著業(yè)務(wù)領(lǐng)域劃分為多個微服務(wù)澈吨,共同組成了復(fù)雜的軟件系統(tǒng)把敢。而從數(shù)據(jù)庫層面來看,隨著數(shù)據(jù)量的爆發(fā)谅辣,不得不采用分庫分表的方式修赞,降低數(shù)據(jù)庫的壓力。這樣桑阶,就造成多個服務(wù)依賴不同的數(shù)據(jù)庫柏副,那么在同時操作的時候,如何保證事務(wù)蚣录?這就是分布式事務(wù)割择。

簡而言之,分布式事務(wù)就是一個大的事務(wù)由不同的子事務(wù)組成萎河,這些小的事務(wù)操作分布在不同的服務(wù)器節(jié)點(diǎn)上面荔泳,屬于不同的微服務(wù),分布式事務(wù)需要保證同一事務(wù)下的子事務(wù)要么全部成功公壤,要么全部失敗换可,即保證數(shù)據(jù)的最終一致性。

五厦幅、分布式事務(wù)解決方案

在這篇不想用太大的篇幅說一些概念上的東西沾鳄,但是要說 RocketMQ 的分布式事務(wù)實(shí)現(xiàn),所以在這里順便提一下當(dāng)前分布式事務(wù)的集中解決方案:

  • 兩階段提交(2PC)

    兩階段提交(2PC) 是 Oracle Tuxedo 系統(tǒng)提出的 XA 分布式事務(wù)協(xié)議的其中一種實(shí)現(xiàn)方式确憨,參考 《分布式事務(wù)之兩階段提交(2PC)》 译荞。

  • Try-Confirm-Cancle (TCC)

    TCC 是基于嘗試、確認(rèn)休弃、取消來實(shí)現(xiàn)分布式事務(wù)的吞歼,想了解更多,參考 《分布式事務(wù)之補(bǔ)償事務(wù)( TCC )》 塔猾。

  • 本地消息表

    本地消息表 方案最初是ebay提出的篙骡,核心是將需要分布式處理的任務(wù)通過消息日志的方式來異步執(zhí)行。消息日志可以存儲到本地文本、數(shù)據(jù)庫或消息隊(duì)列糯俗,再通過業(yè)務(wù)規(guī)則自動或人工發(fā)起重試尿褪。人工重試更多的是應(yīng)用于支付場景,通過對賬系統(tǒng)對事后問題的處理得湘。

image.png

除了上述外杖玲,還有一些解決方案,比如阿里 SEATA 淘正,SAGA方案和最大努力通知...感興趣同學(xué)們可以自行了解,當(dāng)然還有我們這篇要說的 MQ 事務(wù)摆马。

六、MQ 事務(wù)

RocketMQ 是阿里開源的一款高性能鸿吆、高吞吐量的分布式消息中間件囤采,基于消息異步方式提供了對分布式事務(wù)的支持,實(shí)現(xiàn)事務(wù)最終一致性伞剑。

下面是 RocketMQ 事務(wù)消息的基本流程交互圖:

image.png

如圖其中分為兩個流程:正常事務(wù)消息的發(fā)送及提交斑唬、事務(wù)消息的補(bǔ)償流程。

1.事務(wù)消息發(fā)送及提交:

(1) 發(fā)送 half 消息黎泣。
(2) 服務(wù)端響應(yīng)消息寫入結(jié)果恕刘。
(3) 根據(jù)發(fā)送結(jié)果執(zhí)行本地事務(wù)(如果寫入失敗,此時half消息對業(yè)務(wù)不可見抒倚,本地邏輯不執(zhí)行)褐着。
(4) 根據(jù)本地事務(wù)狀態(tài)執(zhí)行 Commit 或者 Rollback( Commit 操作生成消息索引,消息對消費(fèi)者可見)

流程圖如下:

image.png

2.補(bǔ)償流程:

(1) 對沒有 Commit/Rollback 的事務(wù)消息( pending 狀態(tài)的消息)托呕,從服務(wù)端發(fā)起一次“回查”
(2) Producer收到回查消息含蓉,檢查回查消息對應(yīng)的本地事務(wù)的狀態(tài)
(3) 根據(jù)本地事務(wù)狀態(tài),重新Commit或者Rollback

其中项郊,補(bǔ)償階段使用定時器回查方式用于解決消息 Commit 或者 Rollback 發(fā)生超時或者失敗的情況馅扣。

七、RocektMQ 事務(wù)消息的使用

如上着降,小伙伴們應(yīng)該對 RocketMQ 的事務(wù)消息有了一定的了解差油,下面看下如何在開發(fā)場景下如何使用。

發(fā)送事務(wù)消息時和普通的消息區(qū)別是任洞,自己要新建一個 TransactionMQProducer 和對應(yīng)的一個 TransactionListener的實(shí)現(xiàn)蓄喇。

  • TransactionMQProducer
    具體的配置有 group、 nameServer 地址交掏、執(zhí)行本地事務(wù)的線程池和事務(wù)監(jiān)聽器的實(shí)現(xiàn)妆偏。
this.producer = new TransactionMQProducer(config.getGroup());
    this.producer.setNamesrvAddr(config.getNameServer());
    this.producer.setExecutorService(config.getExecutorService());
    this.producer.setTransactionListener(config.getTransactionListener());
  • TransactionListener
    實(shí)現(xiàn) TransactionListener 接口的兩個方法:
    • executeLocalTransaction(Message message, Object o)
      用于執(zhí)行本地事務(wù)的方法。
    • checkLocalTransaction(MessageExt messageExt)
      RocketMQ 回查本地事務(wù)狀態(tài)調(diào)用的方法盅弛。

代碼詳見 ?? : https://github.com/wangning1018/rocketmq-transaction-message-demo

歡迎訪問個人博客 獲取更多知識分享钱骂。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末叔锐,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子罐柳,更是在濱河造成了極大的恐慌掌腰,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件张吉,死亡現(xiàn)場離奇詭異,居然都是意外死亡催植,警方通過查閱死者的電腦和手機(jī)肮蛹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來创南,“玉大人伦忠,你說我怎么就攤上這事「逭蓿” “怎么了昆码?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長邻储。 經(jīng)常有香客問我赋咽,道長,這世上最難降的妖魔是什么吨娜? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任脓匿,我火速辦了婚禮,結(jié)果婚禮上宦赠,老公的妹妹穿的比我還像新娘陪毡。我一直安慰自己,他們只是感情好勾扭,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布毡琉。 她就那樣靜靜地躺著,像睡著了一般妙色。 火紅的嫁衣襯著肌膚如雪桅滋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天燎斩,我揣著相機(jī)與錄音虱歪,去河邊找鬼。 笑死栅表,一個胖子當(dāng)著我的面吹牛笋鄙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播怪瓶,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼萧落,長吁一口氣:“原來是場噩夢啊……” “哼践美!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起找岖,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤陨倡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后许布,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體兴革,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年蜜唾,在試婚紗的時候發(fā)現(xiàn)自己被綠了杂曲。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡袁余,死狀恐怖擎勘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情颖榜,我是刑警寧澤棚饵,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站掩完,受9級特大地震影響噪漾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜藤为,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一怪与、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧缅疟,春花似錦分别、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至桅咆,卻和暖如春括授,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背岩饼。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工荚虚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人籍茧。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓版述,卻偏偏與公主長得像,于是被迫代替她去往敵國和親寞冯。 傳聞我的和親對象是個殘疾皇子渴析,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評論 2 359