分佈式事務以及解決方案

一、何爲分佈式事務

當使用分佈式服務或者微服務的時候,各個重要的功能模塊被分成不同的服務,不同的服務可能寫不同的DB。

例:若就按照最經典的銀行轉賬來說,若A向B轉賬,A扣除錢,B增加錢,扣錢和增加錢屬於不同的服務。

A若先扣完錢,然後通過消息隊列或者是直接通知B,但是這個時候因爲網絡問題,消息發送不出去,重試以後還是發不出去,然後就會導致A扣錢了,B還是沒有增加錢,數據無法保證一致性。

若A先發送消息,然後再扣錢。萬一消息發送成功了,A扣錢失敗,然後就會導致B收到了消息,然後B增加了錢,A並沒有扣錢,導致數據還是無法保證一致性。

以上就是經典的分佈式事務,無法保證數據的一致性。

二、本地數據庫事務

1、ACID(原子性、一致性、持久性、隔離性)

2、InnoDB如何實現數據庫的事務

InnoDB是Mysql數據庫的一個存儲引擎,其還有MyISAM、MEMORY、BDB

原子性和一致性:是通過UndoLog來實現,Undo log的原理是:爲了滿足事務的原子性,在操作任何數據之前,都將數據庫中已有的數據備份在一個地方,這個存儲備份數據的地方就是UndoLog,然後對數據進行修改,若修改出現了問題或者rollback了,則可通過UndoLog備份的數據將數據庫恢復到修改之前的狀態。

持久性:是通過RedoLog實現的,RedoLog記錄的是新數據的備份,在提交事務之前,只需要將RedoLog持久化即可,不需要將數據在數據庫持久化,當數據庫崩潰時,雖然數據庫沒有持久化,但是RedoLog已經持久化了,可通過RedoLog將數據恢復到最新狀態。

隔離性:是通過數據庫鎖機制實現的。

三、分佈式事務的理論基礎

1、CAP定理

C(Consistency一致性)、A(Availiability可用性)、P(Partition Tolerace分區容錯性)

分佈式架構,都得保證分區容錯性,某個節點出問題了,不影響整體的使用。所以現在的分佈式架構要麼選擇AP放棄強一致性,要麼選擇CP,放棄可用性。微服務一般使用的都是AP,放棄了強一致性,用最終一致性代替。而Dubbo使用的是CP,放棄了短暫的可用性,因爲Dubbo使用了zookeeper,zookeeper內部節點採用了選舉模式,當主節點掛掉的時候,會選舉出新的節點,選舉的時間內不可用。

一致性分爲強一致性(CPA中的C)、弱一致性、最終一致性,最終一致性是對弱一致性的增強。

2、BASE定理

BA(Basically available基本可用)、S(Soft state軟狀態)、E(Eventually consistent最終一致性)

BASE定理是對CAP定理的一致性和可用性進行均衡後的結果,該理論的核心是:我們無法保證強一致性,但是各應用根據自身情況通過一定的方式達到最終一致性。

3、強一致性

強一致性是基於X/Open組織定義的DTP標準,該標準定義了規範和API接口,其主要分爲三個角色,AP(應用程序)、RM(資源管理器)、TM(事務管理器)。舉一個下單的例子:

首先應用程序會向事務管理器註冊全局事務,然後在對應的資源管理器執行下單或者鎖庫存的操作,因爲資源管理器都實現了XA接口,因此事務管理器能根據執行的結果來決定是繼續提交還是回滾。

注意:數據庫是資源管理器,但是資源管理器不一定是數據庫,例如消息中間件也是資源管理器,mysql和oracle中都對XA進行了實現。 

四、常用的幾種分佈式事務的處理

1、兩階段提交(2pc,也叫XA-transaction)   -------全局事務解決方案

其基於XA協議,所以也叫作XA-transaction,其分爲兩個步驟:

1.1(準備階段)、事務管理器會要求涉及事務的數據庫預操作(precommit),並給出反饋是否可執行,預操作包括根據自身情況判斷是否能執行該事務,若能執行則執行undo和redo寫入到事務日誌中,等待下一步的操作;

1.2(提交階段)、事務管理器根據反饋要求所有的數據庫進行提交或者回滾操作。

缺點:

        單點故障:若第二階段事務管理器掛掉了,資源服務器就會一直阻塞,導致數據庫不可用;

        數據不一致:還是在第二階段,事務管理器通知所有的數據庫進行提交操作,由於網絡抖動或者其他的原因導致部分數據庫進行了提交操作,部分沒收到,沒提交,導致數據不一致性。

       同步阻塞的問題:在準備就緒以後,資源管理器會一直佔用事務涉及的資源,導致資源管理器中的資源一直處於阻塞狀態,直到所有的提交結束,才能釋放資源。

2、三階段提交(3pc)  -------全局事務解決方案

三階段提交是二階段提交的基礎上進行一些優化:參與者和協調者之間加入了超時的狀態;在第一階段和第二階段之間加入了一個準備階段,保證在準備階段以後所有參與者的狀態是一致的。

其步驟分爲三個階段:

CanCommit:事務管理器向所有參與者發送canCommit命令,各參與者檢查自身是否能夠執行事務的提交操作;

PreCommit:若所有的參與者都跟事務管理器返回的都是YES,則代表都可執行事務的提交操作,然後事務管理器向所有參與者發送preCommit命令,參與者開始執行事務,寫undo和redo信息到事務的日誌中,然後反饋執行的結果給事務管理器;若有一個返回NO,則事務管理器向所有的參與者發送abort命令,放棄事務的執行;

DoCommit:事務管理器收到所有的YES返回以後,向參與者發送doCommit,各參與者將預操作變成執行的操作,完成事務,此時若參與者超時未收到協調者的消息,也會執行commit操作,因爲預提交狀態代表都所有的參與者都可執行事務;若事務管理器收到某一個反饋爲NO,則向所有參與者發送rollback命令,參與者根據undo和redo來執行回滾。

優點:減少了單點故障和資源阻塞,參與者如果收不到協調者的命令,會執行commit

缺點:還是會有數據一致性問題:當協調者向參與者發送abort命令時,由於網絡原因導致超時某些參與者還沒收到,則參與者會自己執行commit操作,導致數據不一致性。

3、TCC(補償事務) -------柔性事務解決方案

核心思想:針對每個操作,都要註冊一個與之對應的確認和補償操作。其分爲三個步驟:

try階段:對業務系統做檢測並做資源預留;

confirm階段:對業務系統做業務提交;

cancel階段:取消業務的執行,同時釋放try階段預留的資源

舉個例子:例如你花10塊錢買個蛋炒飯,try階段你的10塊錢被凍結,那個蛋炒飯也被凍結,confirm階段你的錢被付給商家,蛋炒飯給你,如果confirm階段出錯了,就會調用cancel對你的錢和商家的蛋炒飯解凍。

優點:操作簡單

缺點:2.3步都可能失敗,而且還要寫很多補償代碼。

4、本地消息表   -------基於可靠消息的分佈式事務解決方案

該方法也是達到最終一致性的解決分佈式事務的一種方案。其核心思想是:將分佈式事務拆分成本地事務進行執行。

4.1、消息生產者會在本地建立一張消息表,然後將執行業務和寫消息到本地消息表這兩個操作都寫在一個事務裏,這樣有一個失敗了都事務回滾。

4.2、定時器去輪詢數據庫消息表狀態爲待發送的消息,然後發送給MQ

4.3、MQ存儲消息,然後返回ack給生產者,生產者收到ack以後將消息表中的消息狀態標記爲已發送

4.4、MQ發送消息給消費者,消費者消費進行業務處理

該方案的前提是消費者消費做了冪等,不重複消費消息,業務處理邏輯不能有問題,可以系統有問題,但是不能業務邏輯有問題,不然沒法做到一致性了。

該方案以及以下的事務消息主要解決的是生產者本地事務執行成功消息發送成功兩者一致性的問題。

5、利用消息隊列的事務消息功能   -------基於可靠消息的分佈式事務解決方案

目前事務消息都是依賴於消息隊列,目前已知支持事務消息的消息隊列就是RocketMQ,其從4.3(2018年8月發佈)版本以後就支持事務消息了,目前(2019年9月)最新版本的是4.5。

5.1、何爲事務消息?

生產者發送事務消息給消息隊列broke以後,broke將消息持久化並標記爲待發送,這時broke不會將消息推送給消費者,消費者主動拉取的時候也看不到標記爲待發送的消息。等broke根據生產者的反饋以後將消息的狀態修改爲已發送以後,該消息就跟可被消費者消費或者被broke推送給對應的消費者。

5.2、事務消息如何解決分佈式事務

5.2.1、生產者發送Half消息給MQ;

5.2.2、MQ收到消息以後將消息本地存儲爲待發送;

5.2.3、生產者收到Half消息發送結果的回執以後,根據回執結果進行下一步,如果回執是失敗的,則沒有下一步,如果回執是成功的,則生產者執行本地事務,根據執行結果發送commit或者rollback給MQ;

5.2.4、MQ根據收到的執行結果進行下一步,如果收到的是commit,則將half消息標記爲可發送,將消息下發給consumer端,如果收到的是rollback,則刪除本地的half消息。如果MQ一直收不到生產者端的執行結果,會自己間隔一定時間去詢問執行結果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章