RocketMQ 分佈式事務消息

1、什麼是事務

事務是將一次執行過程當中所涉及的全部操做歸入到一個不可分割的執行單元,組成事務的全部操做只有在全部操做均能正常執行的狀況下才能提交,只要其中任一操做執行失敗,都將致使整個事務的回滾。一句話來講,就是保證多個操做要麼都作,要麼都不作。同時一旦事務提交,則其所作的修改會永久保存到數據庫。html

2、事務的四個特性(ACID)

  • A:原子性(Atomicity)
    一個事務(transaction)中的全部操做,要麼所有完成,要麼所有不完成,不會結束在中間某個環節。事務在執行過程當中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務歷來沒有執行過同樣。
  • C:一致性(Consistency)
    事務的一致性指的是在一個事務執行以前和執行以後數據庫都必須處於一致性狀態。若是事務成功地完成,那麼系統中全部變化將正確地應用,系統處於有效狀態。若是在事務中出現錯誤,那麼系統中的全部變化將自動地回滾,系統返回到原始狀態。
  • I:隔離性(Isolation)
    指的是在併發環境中,當不一樣的事務同時操縱相同的數據時,每一個事務都有各自的完整數據空間。由併發事務所作的修改必須與任何其餘併發事務所作的修改隔離。事務查看數據更新時,數據所處的狀態要麼是另外一事務修改它以前的狀態,要麼是另外一事務修改它以後的狀態,事務不會查看到中間狀態的數據。
  • D:持久性(Durability)
    指的是隻要事務成功結束,它對數據庫所作的更新就必須永久保存下來。即便發生系統崩潰,從新啓動數據庫系統後,數據庫還能恢復到事務成功結束時的狀態。

3、InnoDB 事務實現

基於衡量事務的四個特性,InnoDB 實現事務實際上就是 4 個特性的實現。java

  • 原子性git

    • 在 MySQL 中有不少類型的日誌,二進制日誌、查詢日誌、錯誤日誌、慢查詢日誌等等。除了這些日誌,還提供了兩種事務日誌,redo log 用來保證持久性, undo log 是原子性和隔離性實現的基礎。
    • 數據庫每執行一條更新數據的 sql 就會生成一條 undo log,好比 insert 一條數據,就會生出一條 delete 的 undo log。若是事務執行失敗或者調用 rollback 就能夠根據 undo log 作數據回滾。
  • 隔離性github

    • 隔離性是指,事務內部的操做與其餘事務是隔離的,併發執行的各個事務之間不能互相干擾。嚴格的隔離性,對應了事務隔離級別中的Serializable (可串行化),但實際應用中出於性能方面的考慮不多會使用可串行化。
    • InnoDB 採用可重複讀隔離級別,使用 MVCC 和行鎖、間隙鎖實現隔離性。
  • 持久性sql

    • InnoDB做爲MySQL的存儲引擎,數據是存放在磁盤中的,但若是每次讀寫數據都須要磁盤IO,效率會很低。爲此,InnoDB提供了緩存(Buffer Pool),Buffer Pool中包含了磁盤中部分數據頁的映射,做爲訪問數據庫的緩衝:當從數據庫讀取數據時,會首先從Buffer Pool中讀取,若是Buffer Pool中沒有,則從磁盤讀取後放入Buffer Pool;當向數據庫寫入數據時,會首先寫入Buffer Pool,Buffer Pool中修改的數據會按期刷新到磁盤中(這一過程稱爲刷髒)。
    • Buffer Pool的使用大大提升了讀寫數據的效率,可是也帶了新的問題:若是MySQL宕機,而此時Buffer Pool中修改的數據尚未刷新到磁盤,就會致使數據的丟失,事務的持久性沒法保證。
    • 因而,redo log被引入來解決這個問題:當數據修改時,除了修改Buffer Pool中的數據,還會在redo log記錄此次操做;當事務提交時,會調用fsync接口對redo log進行刷盤。若是MySQL宕機,重啓時能夠讀取redo log中的數據,對數據庫進行恢復。redo log採用的是WAL(Write-ahead logging,預寫式日誌),全部修改先寫入日誌,再更新到Buffer Pool,保證了數據不會因MySQL宕機而丟失,從而知足了持久性要求。
    既然redo log也須要在事務提交時將日誌寫入磁盤,爲何它比直接將Buffer Pool中修改的數據寫入磁盤(即刷髒)要快呢?主要有如下兩方面的緣由:
    (1)刷髒是隨機IO,由於每次修改的數據位置隨機,但寫redo log是追加操做,屬於順序IO。
    (2)刷髒是以數據頁(Page)爲單位的,MySQL默認頁大小是16KB,一個Page上一個小修改都要整頁寫入;而redo log中只包含真正須要寫入的部分,無效IO大大減小。
  • 一致性數據庫

    • 一致性是指事務執行結束後,數據庫的完整性約束沒有被破壞,事務執行的先後都是合法的數據狀態。
    • 一致性不只由數據庫自己來保證,同時業務系統也保證數據的一致性。

4、分佈式事務的由來

現代軟件架構隨着業務領域劃分爲多個微服務,共同組成了複雜的軟件系統。而從數據庫層面來看,隨着數據量的爆發,不得不採用分庫分表的方式,下降數據庫的壓力。這樣,就形成多個服務依賴不一樣的數據庫,那麼在同時操做的時候,如何保證事務?這就是分佈式事務。緩存

簡而言之,分佈式事務就是一個大的事務由不一樣的子事務組成,這些小的事務操做分佈在不一樣的服務器節點上面,屬於不一樣的微服務,分佈式事務須要保證同一事務下的子事務要麼所有成功,要麼所有失敗,即保證數據的最終一致性。服務器

5、分佈式事務解決方案

在這篇不想用太大的篇幅說一些概念上的東西,可是要說 RocketMQ 的分佈式事務實現,因此在這裏順便提一下當前分佈式事務的集中解決方案:架構

  • 兩階段提交(2PC)

    兩階段提交(2PC) 是 Oracle Tuxedo 系統提出的 XA 分佈式事務協議的其中一種實現方式,參考 《分佈式事務之兩階段提交(2PC)》併發

  • Try-Confirm-Cancle (TCC)

    TCC 是基於嘗試、確認、取消來實現分佈式事務的,想了解更多,參考 《分佈式事務之補償事務( TCC )》

  • 本地消息表

    本地消息表 方案最初是ebay提出的,核心是將須要分佈式處理的任務經過消息日誌的方式來異步執行。消息日誌能夠存儲到本地文本、數據庫或消息隊列,再經過業務規則自動或人工發起重試。人工重試更多的是應用於支付場景,經過對帳系統對過後問題的處理。

image.png

除了上述外,還有一些解決方案,好比阿里 SEATA ,SAGA方案和最大努力通知...感興趣同窗們能夠自行了解,固然還有咱們這篇要說的 MQ 事務。

6、MQ 事務

RocketMQ 是阿里開源的一款高性能、高吞吐量的分佈式消息中間件,基於消息異步方式提供了對分佈式事務的支持,實現事務最終一致性。

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

image.png

如圖其中分爲兩個流程:正常事務消息的發送及提交、事務消息的補償流程。

1.事務消息發送及提交:

(1) 發送 half 消息。
(2) 服務端響應消息寫入結果。
(3) 根據發送結果執行本地事務(若是寫入失敗,此時half消息對業務不可見,本地邏輯不執行)。
(4) 根據本地事務狀態執行 Commit 或者 Rollback( Commit 操做生成消息索引,消息對消費者可見)

流程圖以下:

image.png

2.補償流程:

(1) 對沒有 Commit/Rollback 的事務消息( pending 狀態的消息),從服務端發起一次「回查」
(2) Producer收到回查消息,檢查回查消息對應的本地事務的狀態
(3) 根據本地事務狀態,從新Commit或者Rollback

其中,補償階段使用定時器回查方式用於解決消息 Commit 或者 Rollback 發生超時或者失敗的狀況。

7、RocektMQ 事務消息的使用

如上,小夥伴們應該對 RocketMQ 的事務消息有了必定的瞭解,下面看下如何在開發場景下如何使用。

發送事務消息時和普通的消息區別是,本身要新建一個 TransactionMQProducer 和對應的一個 TransactionListener的實現。

  • TransactionMQProducer
    具體的配置有 group、 nameServer 地址、執行本地事務的線程池和事務監聽器的實現。
this.producer = new TransactionMQProducer(config.getGroup());
    this.producer.setNamesrvAddr(config.getNameServer());
    this.producer.setExecutorService(config.getExecutorService());
    this.producer.setTransactionListener(config.getTransactionListener());
  • TransactionListener
    實現 TransactionListener 接口的兩個方法:

    • executeLocalTransaction(Message message, Object o)
      用於執行本地事務的方法。
    • checkLocalTransaction(MessageExt messageExt)
      RocketMQ 回查本地事務狀態調用的方法。

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

歡迎訪問 我的博客 獲取更多知識分享。
相關文章
相關標籤/搜索