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