基于数据库的事务消息解决分布式事务方案
轉載請注明出處:http://www.cnblogs.com/lizo/p/8516502.html
概述
當單庫已不能支撐當前業務的時候,我們往往都考慮進行分庫(橫向拆分或者縱向拆分)。但分庫有個無法回避的問題,就是事務問題。網上有很多分布式事務解決方案,例如XA,TCC等,但是最常用,也是改造成本最低就是使用最終一致性來保證分布式事務。
比較常用的就是使用消息中間件(RabbitMq,RocketMq),通過事務消息來解決最終一致性。參考https://zhuanlan.zhihu.com/p/25933039?utm_source=tuicool&utm_medium=referral。
本篇文章將使用數據庫的來達到最終一致性的實現方案。
名詞解釋
- 主庫-拆分前,業務訪問的數據庫
- 分庫-拆分后,部分業務數據放入到分庫中
注:以下有些內容是在使用事務消息(無論是基于數據庫還是基于消息隊列)應該考慮的地方。
基于數據庫的事務消息
事務消息
所謂基于數據庫的事務消息,其實很好理解,就是在數據庫中創建一個類似消息隊列的表,用于保存事務消息。在拆分前,一個事務中,有多個主庫的數據操作。如下圖,
?
但是在拆分數據庫后,有業務被拆分到分庫中去了,這樣,原有的單庫事務被打破,但是通過把拆分出去的業務使用一個事務消息來代替(事務消息表也是在主庫中,所以這里還是單庫事務),后續再通過其他方式去執行該事務消息所對應的業務邏輯即可,這樣,就可以達到最終一致性,如下圖
?
事務消息執行器
前面說到了,事務消息需要一個處理器來進行執行事務消息所對應的業務邏輯。事務處理器應該是順序的去讀取并執行的。
設想一個場景:當出現某一條消息處理失敗,如果執行器要等當前消息執行成功才繼續往后執行(甚至該消息永遠不會處理成功),那么會影響后續消息的執行,導致整個系統出現問題。
因此,消息處理器即要保證消息處理盡可能處理快,又能保證消息最終能執行成功。 在消息執行器中必須設置2個任務:
- 第一個任務,消息處理任務,已最快的速度執行消息,如果消息處理失敗了,跳過該消息繼續執行后面的消息。
- 第二個任務,消息校驗任務,這個任務就是順序檢查消息,保證所有消息都執行成功,如果失敗,進行重試,多次重試失敗以后發出告警以讓人工介入處理。 如下圖
?
注:上圖左邊那個是消息隊列及其處理狀態
消息執行的特性
- 延遲處理性。消息不是實時處理的,而是用過消息執行器來異步執行的。因此,如果在原有邏輯中,需要特別注意后續流程對該消息處理結果是不是有實時依賴性(例如后續業務邏輯中會使用該消息處理結果來做一些計算等)。
- 處理無序性。由于消息不一定是順序執行的,所有保證即使后生成的消息先執行,也不能出現問題。
- 最終成功性。對每條插入的消息,保證該條消息一定要能執行成功
如何確認消息已執行成功
設想,如果分庫業務執行成功(更新分庫),然后去更新消息狀態(主庫),這樣,又是一個夸庫事務,所以,得想其他辦法來避免,最簡單的方法,就是在分庫里面也建一個消息表,保存處理的成功的消息。這樣,通過對比主庫和分庫的消息表,就知道哪些事務消息沒有執行成功
消息處理器基本框架
前面介紹了,消息處理器的核心功能就:
- 獲取消息,并把消息發送給業務放處理
- 保證消息執行的成功?
為了完成上面功能,需要消息處理任務和消息校驗任務,通過定時調度任務來觸發這2個任務(例如,5s觸發一次)
?
消息處理任務
消息處理任務就是通過掃描待處理的消息,然后通知業務系統執行。
?
再次強調,消息處理任務不會管消息是否執行成功。都是按照消息隊列表順序執行下去。
消息校驗任務
校驗任務就是比較主庫和分庫中的消息記錄(主庫中記錄的所有消息,分庫中記錄的執行成功的消息),對執行未成功的消息發起重試,如果多次重試失敗則發出告警,需要人工介入。?
?
和基于消息中間件的事務消息比較
相同點
- 都是采用異步確保最終一致性:
- 可以控制異步執行消息的速率,可以利用RPC調用的負載均衡
- 消息處理都必須支持重試和冪等性
- 事務消息異步執行失敗,都沒辦法回滾產生事務消息的事務?
不同點
消息事務的提交
使用消息中間件,一般都需要在代碼中顯示的編寫提交中間件事務消息的代碼,類似下面
public boolean transaction(String text){try {發送事務消息執行本地事務提交事務消息return true;} catch (TmcException e) {return false;} }但在實際項目中,事務的傳播性的問題(spring 的事務注解是支持事務的傳播性),就需要修改業務代碼。但使用基于數據庫的消息隊列就沒有這個問題
@Transactional public void publishAS(String text){ 執行本地事務邏輯插入事務消息 }所以在既有代碼改造上(特別是復雜系統中),使用數據庫的事務消息可以減少代碼的改動
不需要回調check
我們知道,在使用消息中間件的時候,都需要實現一個回調接口,當事務消息長時間沒有commit的時候,會調用該接口來確認是否需要commit(例如發送消息成功,但是在commit的時候網絡不可用)。而基于數據局的事務消息隊列就沒有這個問題
更多的數據庫訪問資源
基于數據庫的事務消息也有一個比較明顯的缺點:
- 占用更多的數據庫空間和數據庫訪問資源
- 需要額外編寫DAO層代碼
小結
基于數據庫和基于消息隊列的事務消息的基本思路都一樣,使用最終一致性來避免分布式事務帶來的額外系統復雜性和代碼開銷。基于數據庫的事務消息在既有業務改造中,代碼變動較小,也不需要額外的引入消息中間件,但是帶來的問題就是對數據庫更多的訪問。而基于消息中間件的問題就是如何避免在與消息中間件交互的出現問題的時候如何應對。當然,以上只是我個人理解,如果系統有什么設計不合理或者有改進的地方,歡迎討論。
轉載于:https://www.cnblogs.com/lizo/p/8516502.html
總結
以上是生活随笔為你收集整理的基于数据库的事务消息解决分布式事务方案的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 11G数据库导入10G的操作实践
- 下一篇: python操作word 查找_Pyth