談談你對RocketMQ分布式事務原理的理解
?有位工作五年的小伙伴在面試的時候被問到RocketMQ的分布式事務實現原理。他說他只知道RocketMQ能夠支持事務,但是沒有了解過它的事務實現原理。
今天,我給大家分享一下我對這個問題的理解。
1.分布式事務應用場景
隨著應用的拆分,從單體架構變成分布式架構,那么每個服務或者模塊也會有自己的數據庫。一個業務流程的完成需要經過多次的接口調用或者多條MQ消息的發送。
浪漫和悲觀并不沖突,我時常消極但又依舊覺得生活很美好,哪怕只是一束光照進了房間,也要認真對待它的到來。
那么問題來了,如果是執行多條SQL語句,數據庫的本地事務可以保證原子性。
浪漫和悲觀并不沖突,我時常消極但又依舊覺得生活很美好,哪怕只是一束光照進了房間,也要認真對待它的到來。
但,如果是一條SQL操作,再加一條MQ的操作,如何才能把它們兩個放在同一個邏輯單元里面執行呢?是先執行SQL還是先發送MQ呢?
浪漫和悲觀并不沖突,我時常消極但又依舊覺得生活很美好,哪怕只是一束光照進了房間,也要認真對待它的到來。
我們來分析一下情況,如果是先發送MQ消息,再執行SQL。這個時候就要分為兩種情況:
第1種情況:如果發送MQ失敗了,當然SQL也就不會執行了。
第2種情況:如果發送MQ成功了,而本地數據庫SQL執行失敗。比如出現了網絡異常,主鍵重復或者字段超長等等。
浪漫和悲觀并不沖突,我時常消極但又依舊覺得生活很美好,哪怕只是一束光照進了房間,也要認真對待它的到來。
也就是說,下游的業務系統拿到了最新的數據,而自己本地的數據庫反而沒有。這個時候,本地數據庫的數據跟其他系統已經登記的數據就不一樣了,而發出去的消息又不可能撤回,有可能已經被消費了,這個叫做覆水難收。
浪漫和悲觀并不沖突,我時常消極但又依舊覺得生活很美好,哪怕只是一束光照進了房間,也要認真對待它的到來。
因此,在分布式應用場景中,我們需要調整一下代碼執行流程,也就是說必須先操作本地數據庫,再發送MQ消息。如果本地數據庫SQL執行成功,就算MQ消息發送失敗,MQ還可以重發。
2.分布式事務實現原理
那基于上面的應用場景,應該如何設計發送消息的流程,才能讓這兩個操作要么都成功,要么都失敗呢?
其實,可以參照XA兩階段提交的思想,把發送消息分成兩步,然后把操作本地數據庫也包括在這個流程中。那么,在介紹原理之前,先科普一下兩個新的概念:
1、半消息(Half Message):也就是暫不能投遞消費者的消息。發送方已經將消息成功發送到了 MQ 服務端,但是服務端未收到生產者對這條消息的二次確認,這個時候,這條消息會被標記為“暫不能投遞”狀態。
2、消息回查(Message Status Check):由于網絡閃斷、生產者應用重啟等原因,導致某條事務消息的二次確認丟失,MQ 服務端通過掃描發現某條消息長期處于“半消息”時,需要主動向消息生產者詢問該消息的最終狀態,要么是Commit,要么Rollback。
下面給大家介紹一下RocketMQ的分布式事務實現原理,如圖所示,一共分為七個步驟:
浪漫和悲觀并不沖突,我時常消極但又依舊覺得生活很美好,哪怕只是一束光照進了房間,也要認真對待它的到來。
第一步:生產者向 MQ 服務端發送消息。
第二步:MQ 服務端將消息持久化成功之后,向發送方 ACK 確認消息已經發送成功,此時消息為半消息。
第三步:發送方開始執行本地數據庫事務邏輯。
第四步:發送方根據本地數據庫事務執行結果向 MQ Server 提交二次確認,MQ Server 收到 Commit 狀態則將半消息標記為可投遞,訂閱方最終將收到該消息;MQ Server 收到 Rollback 狀態則刪除半消息,訂閱方將不會接受該消息。
第五步:在斷網或者是應用重啟的特殊情況下,按步驟4提交的二次確認最終未到達 MQ Server,經過固定時間后 MQ Server 將對該消息發起消息回查。
第六步:發送方收到消息回查后,需要檢查對應消息的本地事務執行的最終結果。
第七步:發送方根據檢查得到的本地事務的最終狀態再次提交二次確認,MQ Server 仍按照步驟4對半消息進行操作(Commit/Rollback)。
好了,以上就是我對RocketMQ分布式事務的理解。