一,分布式事務簡介
在當前互聯網,大數據和人工智能的熱潮中,傳統企業也受到這一潮流的沖擊,紛紛響應國家“互聯網+”的戰略號召,企業開始將越來越多的應用從公司內網遷移到雲端和移動端,或者將之前孤立的IT系統聯網整合,或者將原來厚重的企業應用拆分重組,獨立成一個個輕量級的應用對外提供服務,這對傳統的業務處理的數據一致性,帶來了嚴重的挑戰,我們已經身處一個分布式的計算環境,分布式事務的需求越來越普遍。
舉一個例子,某行業電商網站經過幾年的發展,業務數據累積越來越多,查詢越來越慢。經過內部評審分析,認為系統的瓶頸就是數據庫壓力過大,如果要解決這問題,必須分表分庫,比如將訂單,商品,用戶分布到不同的數據庫去,但這樣隨之帶來一個問題,原來處理業務的時候使用的是本地事務,分庫后就需要使用分布式事務了。
那么應該如何實現分布式事務呢?
這里我們需要明確一點,並非數據庫天然就是分布式的在執行操作的,事務都是在一個數據庫實例上進行的,如果要執行一個分布式事務的操作,那么就需要協調多個分散的數據庫上執行的事務操作。所以在分布式事務中,有2個概念:
l Distributed Transaction Resource Owner (簡稱DTR):
n --分布式事務資源服務器,擁有事務資源的服務器,如絕大部分關系數據庫,一些消息隊列,或者一些能夠執行類似事務操作的應用。
l Distributed Transaction Coordinate Controller (簡稱DTC):
n --分布式事務協調控制器,它協調控制分布式事務環境中的事務資源服務器,發送指令給它們並且處理事務資源服務器返回的結果。
二,分布式事務的實現層面
在分布式事務的具體實現層面,可以在數據庫層直接實現,也可以在應用服務層面實現。如果是在應用服務層面實現,本質上它也可能是調用本地的數據庫事務。
下面是DTR與DTC的拓撲關系圖:
基於關系數據庫層面接口實現的分布式事務
基於數據庫的分布式事務,一般在應用程序的數據層調用系統的分布式事務組件,應用程序執行本地事務的時候,先選舉出一個分布式事務協調器,然后協調器來協調各個本地事務的執行。由於是在應用程序的數據層進行的調用,所以它對遠程數據庫的操作是在本地進程內的。如果你的應用部署在多台服務器上,那么在每一台操作數據庫的應用服務器上都要安裝運行分布式事務協調器服務。
基於應用服務層面實現的分布式事務
基於應用服務層面的分布式事務,是在應用服務層面進行的事務控制,它同樣會有分布式事務協調控制器,和事務資源服務器。與基於數據庫層面的分布式事務不同,事務的類型支持更廣泛,比如消息隊列訪問,文件寫入或者具有補償操作的業務應用程序,都可以成為事務資源,並且不要求這些事務資源本身支持分布式事務。舉個例子,事務資源A是Windows上的SQLSERVER數據庫,事務B是Linux上的MySQL數據庫,這時候事務B就沒法使用Windows上的事務協調控制器MSDTC了。而基於服務層面的分布式事務,可以解決這個問題。
三,分布式事務的2階段提交協議(2PC)
第一階段(1PC):提交投票階段
協調器向事務資源服務器發出 CanCommit 的是否可以提交事務的詢問指令,事務資源服務器收到此指令后,准備好要提交的事務資源,再向協調器回復 YES;如果沒有准備好,比如執行事務中的操作出現了錯誤,應該回復 NO.如果某DTR無法回復,DTC也認為該DTR的結果是NO.
第一階段,全部回復為YES,代表各個事務資源服務器均已經准備好了提交。
第一階段,事務資源服務器DTR-2回復為NO,如果DTC等待DTR-2超過設定時間都沒有得到回復,或者DTR-2與DTC斷開了連接,也認為DTR-2的結果是NO
第二階段(2 PC):提交或終止階段
協調器統計所有事物資源服務器的回復數量,如果全部回復為YES,則向所有事物資源服務器發出Commit指令,否則,發出Abort指令。資源服務器收到指令后,執行相應的操作。
二階段提交事務的數據不一致問題
在第二階段(2PC),如果DTR沒有收到DTC的指令改怎么辦呢?
如果等到超時都沒有收到DTC的指令,DTR處於“可以提交”或者“不可以提交”的雙重狀態,也就是提交狀態不可知。假設DTR1沒有收到DTC的提交指令或者撤銷指令,DTR1可以假設DTR2也不會收到指令,因為此時大概率是DTC宕機或者網絡整體不良,那么DTR1最佳的做法是回滾事務。
但是,如果僅僅是DTR1受網絡影響沒有收到提交指令,而DTR2收到了提交指令,那么DTR1回滾事務,DTR2提交了事務,整個分布式事務就是失敗的,數據發生了不一致。
因此,2階段提交的分布式事務不是高可靠的分布式事務控制模型,需要在事務資源的提交環節做更多的驗證,這便是3階段提交的分布式事務。
不過,對於大部分系統,2階段提交的分布式事務已經能夠滿足應用了,因為通常情況下,都是基於數據庫應用層實現的分布式事務,並且各個事務資源節點都在同一個局域網內,發生網絡不穩定的概率非常小,並且現在不少數據庫都會做高可靠性的數據庫集群,發生宕機的可能性也非常小,最終出現數據不一致的概率也就非常小了。
如果系統的應用環境不能滿足上面說的任何一個條件,即分布式事務的控制不是在數據庫應用層,子系統不在一個局域網,或者數據庫沒有做高可靠的集群,並且對於系統的事務一致性要求非常高,那么應該使用3階段提交協議來實現分布式事務。
四,分布式事務的3階段提交協議(3PC)
對2階段提交協議的分析我們發現,2PC的事務提交階段狀態是不確定的,整個事務容易出現不一致的情況。所以,我們隊2PC的提交階段,進一步拆分成“預提交”階段和提交階段,增加事務提交狀態的確認過程。
第一階段(1PC):提交投票階段
協調器向事務資源服務器發出 CanCommit 的是否可以提交事務的詢問指令,事務資源服務器收到此指令后,准備好要提交的事務資源,再向協調器回復 YES;如果沒有准備好,比如執行事務中的操作出現了錯誤,應該回復 NO.如果某DTR無法回復,DTC也認為該DTR的結果是NO.
該階段的處理過程跟2階段提交協議的第一階段是一樣的,處理流程圖參考前面,此略。
第二階段(2 PC):預提交或終止階段
預提交事務
協調器(DTC)統計所有事務資源服務器(DTR)的回復數量,如果全部回復為YES,則向所有事物資源服務器發出PreCommit指令,否則,發出Abort指令。資源服務器收到指令后,執行相應的操作。
在第二階段,如果DTR收到PreCommit指令,則向DTC回復ACK消息,表示收到了指令,准備提交,接着,進入第三階段,等待最終的提交指令。
終止事務
在第二階段,如果在第一階段有節點異常,DTC發出撤銷指令,DTR收到了撤銷指令,那么它執行回滾本地事務的操作。如果由於網絡原因,某個DTR一直等到超時都沒有收到PreCommit指令,那么它執行Abort撤銷指令,回滾本地事務。
第三階段(3 PC):提交或終止階段
提交分布式事務
協調器(DTC)統計所有事務資源服務器(DTR)在第二階段的回復數量,如果全部回復為ACK,則向所有DTR發出Commit指令。DTR收到指令后,執行事務提交操作,並返回Commit Done消息,DTC收到此消息,結束整個分布式事務過程。
回滾分布式事務
協調器(DTC)統計所有事務資源服務器(DTR)在第二階段的回復數量,如果未收到全部回復為ACK,則它認為有節點可能出現了網絡故障,此節點沒有收到PreCommit指令或者雖然收到了卻沒有回復ACK,測試DTC應該向所有DTR節點發出撤銷指令。各DTR收到撤銷指令后,回滾本地事務,然后回復消息,DTC完成本次事務過程。
三階段提交事務也並不完美
考察第3階段的提交分布式事務的情況,DTR1收到了Commit指令,但是由於網絡原因,DTR2沒有收到此指令,那么DTR2是提交本地事務還是回滾本地事務?
站在DTR2的角度,它在本階段可能收到Commit指令,也可能收到Abort指令,那么它既可以提交本地事務也可以回滾本地事務,兩種操作是不確定的,所以,3階段提交協議,仍然不是完美的,不能百分之百保證數據的最終一致性。
既然3階段提交協議仍然有不確定性,那么相比2階段提交協議有什么意義呢?
仔細想下,DTR2已經進入第3階段了,那么肯定其它DTR都進入了第3階段,而進入第3階段的前提是各DTR節點都收到過PreCommit指令,都是已經准備好提交只等最后的提交指令了,否則各節點在第二階段應該收到撤銷指令,不會再進入第三階段。既然各DTR節點都進入了第三階段,它們都准備好提交事務了,那么即使沒有收到最終的Commit指令,DTC發出Commit指令也是大概率的。所以,從概率上講,如果在第三階段,DTR沒有收到Abort撤銷指令,也沒收到Commit提交指令,那么它默認應該指向Commit指令,提交本地事務。相比第二階段某DTR節點沒有收到指令而認為應該收到PreCommit指令的概率,要大得多。
關於第三階段沒有收到指令而應該大概率執行Commit指令的問題,理解起來可能有點困難,我給同事講的時候大部分同事也難以理解,可能是我表述的問題,大家有更好的解釋方式,歡迎交流,不勝感激!
5,實現 3階段提交的分布式事務
本文將介紹一個基於服務層面而不是數據庫層面的,3階段提交的分布式事務中間件的設計開發過程。這個中間件必須解決下面幾個問題:
l 通信組件—分布式事務控制器(DTC),分布式事務資源服務器(DTR)都是獨立的服務,這些服務部署在不同的通信節點,它們之間需要進行可靠的網絡通信,因此通信組件是基礎;
l 數據訪問組件—提供基礎的數據讀寫操作,並且能夠操作本地事務。
l 服務組件—將DTC,DTR的功能代碼編寫為相應的SOA服務組件
l 關系數據庫—具有事務功能的關系數據庫,可以是嵌入式的本地數據庫,比如SQLite,也可以是服務器客戶機模式的網絡數據庫,比如SQLSERVER。
各組件的關系圖如下:
PDF.NET的消息服務框架(MSF)具有開發服務組件基礎的接口和一套消息通信組件,同時還有一個服務容器,可以承載本篇文章說的分布式事務控制器DTC,分布式事務資源服務DTR這些服務應用,同時PDF.NET還有一個強大的數據訪問組件 PDF.NET SOD,下一篇文章,我們將來具體討論基於MSF和SOD的3階段分布式事務應用的實現過程,它的源碼已經發布在 https://github.com/bluedoctor/MSF-DistTransExample ,大家可以先睹為快。