享學課堂特邀作者:老顧
轉載請聲明出處!
前言
在系統變的複雜後,分布式、微服務等架構技術,就要考慮到應用在系統中了。尤其資料量大了後,就需要對資料庫進行拆分。
如:注冊的使用者資料,量大了後,就需要考慮分庫分表
一旦資料庫進行了分拆,那就出現很多頭疼的問題,其中之一就是事務問題。那我們就來看看問題是怎麼出現的?
場景
先來上個圖

進行資料拆分後,就類似上面的架構,可以看老顧上一篇文章關于【Mysql的高可用架構】
上圖中我們就拿使用者的資料進行舉例,使用者量一旦幾千萬時,就需要進行分庫分表;上圖就分了3個庫,每個庫都保證了高可用。
這樣的架構設計,會遇到事務問題,我們來看看具體的業務場景:使用者A轉賬100元給使用者B,這個業務比較簡單,我們來分析一下裡面具體的步驟:
1、使用者A的賬戶先扣除100元 2、再把使用者B的賬戶加100元
邏輯很簡單,上僞代碼
代碼也是比較清晰的,感覺沒有什麼問題,那我們來分析一下問題在哪?
問題
我們看到在轉賬業務中,有兩步,一個是操作使用者A扣錢,一個是操作使用者B加錢
如果在同一個資料庫中進行,可以保證這兩步操作,要麼同時成功,要麼同時不成功。這樣就保證了轉賬的資料一緻性。
但是如果使用者A的資料在叢集A中,使用者B在叢集B中呢?因為他們不在同一個事務中;如使用者A扣款成功,但使用者B加錢失敗了;那就坑了,資料不完整了。
類似這種問題在微服務架構會更多,因為各個服務都是獨立的子產品,都是遠端調用,都沒法在同一個事務中,都會遇到事務問題。
那怎麼解決?網上有一些方案,如:兩階段送出,TCC等,還有常用就是最終一緻性方案。老顧就給大家介紹一下如何利用消息中間件去解決。那我們就把方案調整一下,加入消息中間件,看看如何優化。
消息中間件方案
上圖就是利用消息中間件的方式,把扣款業務和加錢業務異步化,扣款成功後,發送“扣款成功消息”到消息中間件;加錢業務訂閱“扣款成功消息”,再對使用者B加錢
系統怎麼知道給使用者B加錢呢?是消息體裡面包含了源賬戶和目标賬戶ID,以及錢數
這個時候也許小夥伴們會問,應該也有問題吧:場景一:先扣款後發消息
先扣款再發送消息,萬一發送消息失敗了,那使用者B就沒法加錢
那把順序調整一下場景二:先發消息,後扣款
扣款成功消息發送成功,但使用者A扣款失敗,可加錢業務訂閱到了消息,使用者B加了錢
大家應該發現了問題所在,也就是沒法保證扣款和發送消息,同時成功,或同時失敗;導緻資料不一緻。
RocketMq事務方案
因為上面的問題,RocketMq消息中間件把消息分為兩個階段:Prepared階段和确認階段、Prepared階段(預備階段)
該階段主要發一個消息到rocketmq,但該消息隻儲存在commitlog中,但consumeQueue中不可見,也就是消費端(訂閱端)無法看到此消息。
commit/rollback階段(确認階段)
該階段主要是把prepared消息儲存到consumeQueue中,即讓消費端可以看到此消息,也就是可以消費此消息。
我們用圖來說明下:
整個流程:
1、在扣款之前,先發送預備消息2、發送預備消息成功後,執行本地扣款事務3、扣款成功後,再發送确認消息4、消息端(加錢業務)可以看到确認消息,消費此消息,進行加錢
确認消息說明
注意:上面的确認消息可以為commit消息,可以被訂閱者消費;也可以是Rollback消息,即執行本地扣款事務失敗後,送出rollback消息,即删除那個預備消息,訂閱者無法消費
我們來分析一下異常場景:
**異常1:**如果發送預備消息失敗,下面的流程不會走下去;**這個是正常的。異常2:**如果發送預備消息成功,但執行本地事務失敗;這個也沒有問題,**因為此預備消息不會被消費端訂閱到,消費端不會執行業務。異常3:**如果發送預備消息成功,執行本地事務成功,但發送确認消息失敗;這個就有問題了,因為使用者A扣款成功了,但加錢業務沒有訂閱到确認消息,無法加錢。這裡出現了資料不一緻。
那RocketMq是怎麼解決的呢?
RocketMq回查
RocketMq如何解決上面的問題,核心思路就是【狀态回查】,也就是RocketMq會定時周遊commitlog中的預備消息。
因為預備消息最終肯定會變為commit消息或Rollback消息,是以周遊預備消息去回查本地業務的執行狀态,如果發現本地業務沒有執行成功就rollBack,如果執行成功就發送commit消息。
上面的異常3,發送預備消息成功,本地扣款事務成功,但發送确認消息失敗;因為RocketMq會進行回查預備消息,在回查後發現業務已經扣款成功了,就補發“發送commit确認消息”;這樣加錢業務就可以訂閱此消息了。
這個思路其實把異常2也解決了,因為本地事務沒有執行成功,RocketMQ回查業務,發現沒有執行成功,就會發送RollBack确認消息,把消息進行删除。
回查判斷業務是否成功
小夥伴們在回查業務中,如何判斷本地事務是否執行成功?
如果本地事務執行了很多張表,那是不是我們要把那些表都要進行判斷是否執行成功呢?這樣是不是太麻煩了,而且和業務很耦合。
有沒有更好的方式呢?就是設計一張Transaction表,将業務表和Transaction綁定在同一個本地事務中,如果扣款本地事務成功時,Transaction中應當已經記錄該TransactionId的狀态為「已完成」。當RocketMq回查時,隻需要檢查對應的TransactionId的狀态是否是「已完成」就好,而不用關心具體的業務資料。
總結
上面就是老顧介紹的RockMq的分布式方案,至于消費端(加錢業務)需要考慮幂等設計,之前老顧的文章【何為幂等?如何設計?】有介紹,小夥伴自行查閱。
還有一點,留一個問題,如果我們不用RockMq消息中間件,而是用普通的消息中間件如:RabbitMq,這怎麼去設計呢?
好了,今天就介紹到這裡,謝謝!!!
最後
既已說到spring cloud alibaba,那對于整個微服務架構,如果想要進一步地向上提升自己,到底應該掌握哪些核心技能呢?
就個人而言,對于整個微服務架構,像RPC、Dubbo、Spring Boot、Spring Cloud Alibaba、Docker、kubernetes、Spring Cloud Netflix、Service Mesh等這些都是最最核心的知識,架構師必經之路!下圖,是自繪的微服務架構路線體系大綱,如果有還不知道自己該掌握些啥技術的朋友,可根據小編手繪的大綱進行一個參考。
如果覺得圖檔不夠清晰,也可來找小編分享原件的xmind文檔!
且除此份微服務體系大綱外,我也有整理與其每個專題核心知識點對應的最強學習筆記:
- 出神入化——SpringCloudAlibaba.pdf
- SpringCloud微服務架構筆記(一).pdf
- SpringCloud微服務架構筆記(二).pdf
- SpringCloud微服務架構筆記(三).pdf
- SpringCloud微服務架構筆記(四).pdf
- Dubbo架構RPC實作原理.pdf
- Dubbo最新全面深度解讀.pdf
- Spring Boot學習教程.pdf
- SpringBoo核心寶典.pdf
- 第一本Docker書-完整版.pdf
- 使用SpringCloud和Docker實戰微服務.pdf
- K8S(kubernetes)學習指南.pdf
需要下載下傳的請**點選傳送門:《出神入化——SpringCloudAlibaba》**
另外,如果不知道從何下手開始學習呢,小編這邊也有對每個微服務的核心知識點手繪了其對應的知識架構體系大綱,不過全是導出的xmind檔案,全部的源檔案也都在此,照樣可免費分享給有需要的你!
xTbFJ1cmN1R2dB)**
[外鍊圖檔轉存中…(img-FsmJe3Jo-1623726888592)]
另外,如果不知道從何下手開始學習呢,小編這邊也有對每個微服務的核心知識點手繪了其對應的知識架構體系大綱,不過全是導出的xmind檔案,全部的源檔案也都在此,照樣可免費分享給有需要的你!