如何利用事務消息實現分佈式事務
一說起事務,容易聯想到數據庫。我們日常使用事務的場景,絕大部分都是在操作數據庫的時候。像 MySQL、Oracle這些主流的關係型數據庫,也都提供了完整的事務實現。
那消息隊列爲什麼也需要事務呢?
很多場景下,往往是一個模塊往另外一個模塊發消息的,往往需要另外一個系統接收到消息後更新數據,消息隊列中的 “事務”,主要解決的消息生產者與消費者之間的數據一致性問題。
栗子
電商購物,用戶將商品添加到購物車,然後幾個商品一起下單,最後支付,完成購物流程,這過程在訂單系統創建訂單後,發送西峽縣給購物車系統,將已下單的商品從購物車中刪除,但是購物車刪除已下單商品這個步驟,並不是用戶下單支付主要流程的必須步驟,因此使用消息隊列來異步清理購物車是更加合理的設計。
訂單系統
訂單系統,主要做了兩件事:
- 在訂單庫中插入一條訂單數據,創建訂單。
- 發消息給消息隊列,消息中的內容是剛創建的訂單。
購物車系統
購物車系統其實也主要做兩件事:
- 用戶將商品添加購物車時,購物車系統中新增一條商品數據。
- 購物車系統收到了訂單創建成功消息,清理已生成訂單的商品數據。
這樣就有一個需要解決的問題,需要保證訂單庫和購物車這兩個庫數據一致性。
需要保證購物車系統狀態是一致的,要麼都成功,要麼都失敗,不允許一個成功一個失敗的狀態。
事務的4個屬性
事務必須具有原子性,一致性,隔離性,持久性,也稱爲 ACID 特性。
- 原子性
原子性,指的是一個事務操作不可分割,要麼成功,要麼失敗,不能有一半成功,一半失敗的情況。
- 一致性
一致性,是指這些數據在事務執行完成這個時間點之前,讀到的一定是更新前的數據,之後讀到的一定更新後的數據,不應該存在一個時刻,讓用戶讀到更新過程中的數據。
- 隔離型
隔離性,是指一個事務的執行不能被其他事務干擾,即一個事務內部的操作及使用數據對正進行的其他事務是隔離的,併發執行的各個事務之間不能相互干擾。
- 持久性
持久性,是指一個事務一旦完成事務,後續的其他操作和故障不會對事務的結果產生任何影響。
什麼是分佈式事務?
分佈式事務就是要在分佈式系統中的實現事務。在分佈式系統中,在保證可用性和不嚴重犧性能的前提下,光是要實現數據的一致性就已經非常困難了,所以出現了很多“殘血版”的一致性,比如順序一致性、最終一致性等等。
CAP 原理
CAP 原理,是指分佈式系統中,Consistency(一致性),Availability(可用性),Partion tolerance(分區容錯性),三者不可兼得。
- 一致性(C)
在分佈式系統中所有的數據備份,同一個時刻是同樣的。
- 可用性(A)
在集羣中一部分節點故障,集羣整體是否還能響應客戶端的讀寫請求。
- 分區容錯性 (P)
以實際效果而言,分區相當於對通訊的實現要求,系統如果不能在時限內達成數據一致性,就意味着發生了分區的情況,必須就當前操作 在 C 和 A 之間做出選擇。
如何實現分佈式事務?
實現分佈式事務有 2PC(TWO-phase Commit 也叫量階段提交),TCC(Try-Confirm-Cancel) 和事務消息。
兩階段提交
兩階段提交保障了分佈式事務的原子性:即所有節點要麼全做,要麼全部做,所謂的兩個階段:第一個階段:準備階段;第二階段:提交階段。
1,準備階段
事務協調者給每個參與者(資源管理器)發送 PrePare 消息,每個參與者要麼直接返回失敗(如權限驗證失敗),要麼在本地執行事務,寫本地的 redo 和 undo 日誌,但是不提交。
- 提交階段
如果協調者收到的參與者的失敗消息或者超時,直接給每個參與者發送回滾(Rollback)消息,否則,發送提交(Commit); 參與者根據協調者提交或者回滾操作,釋放所有事物處理過程中的鎖資源。
RocketMQ 中的分佈式事務實現
RocketMQ 中的事務實現中,增加了事務反查的機制來解決時序消息提交失敗的問題,如果 Producer 在提交或者會館事務消息時發生網絡異常,RocketMQ 的 Broker 沒有收到提交或者回滾的請求,Broker 會定期去 Producer 上反查這個事務對應的本地事務的狀態,然後根據反查結果決定提交或者回滾這個事務。
爲了支持這個事務反查機制,我們的業務代碼需要實現一個反查本地事務狀態的接口,告知 RocketMQ 本地事務是 成功還是失敗的。
舉個例子,訂單系統 根據消息中的訂單ID ,在訂單庫中查詢這個訂單是否存在即可,如果訂單存在則返回成功,否則返回失敗, RocketMQ 會自動根據事務反查的結果提交或者回滾事務。