分散式系統漫談【拾】_分散式事務一致性:阿里方案
其實對於生產環境的分散式事務一致,各大網際網路公司都是自己實現的解決方案,總結起來無非是非同步、補償、實時查詢、定期校對幾種模式,大部分場景都是使用到訊息中介軟體。下面介紹下阿里對分散式事務一致的解決方案,內容總結自鍾華老師的《企業IT架構轉型之道 阿里巴巴中臺戰略思想與架構實戰》,嚴重安利這本書,任何想向架構方向發展的同學,此書不可不讀。購書連結如下:
柔性事務
阿里提出了柔性事務的概念。柔性事務的思想就是,儘量在處理分散式事務的時候不引入鎖而增加時間開銷(對比兩階段提交協議),也不要增加其他額外的時間延遲(Paxos協議需要多臺機器確認議案)。考慮通過補償、非同步和樂觀鎖等方式,來實現事務的最終一致性。
補償
柔性事務的補償機制主要由日誌保證。事務日誌記錄事務的開始、結束狀態,可能還包括事務參與者資訊。參與者節點也需要根據重做或回滾記錄redo/undo日誌,當事務重試或回滾時,可以根據這些日誌最終將資料恢復到一致狀態。
非同步
由於引入了非同步操作,因此需要訊息中介軟體保證可靠的訊息傳遞:事務訊息至少投遞一次,可能投遞多次。這還要求訊息處理程式必須實現冪等。冪等就是對於同一操作反覆執行多次,響應或計算的結果不變。
樂觀鎖
可以避免長事務中的資料庫加鎖開銷,大大提升了高併發量下的系統整體效能。其實樂觀鎖的實現,就是類似之前在多執行緒系列文章中講的cas操作,對需要併發更新的資料增加版本號的記錄,每次去更新的時候先進行版本號的比對,版本號一致才進行更新操作。
下面介紹一下阿里的關於分散式事務一致性的三種解決方案:
訊息分散式事務
顧名思義,就是基於訊息中介軟體MQ事務訊息功能特性,達到分散式事務的最終一致。整體流程可以簡單描述如下:
假設有A、B兩個系統需要進行兩個事務的操作。首先A系統傳送一條事務訊息到訊息中介軟體的某個頻道上,B系統訂閱了該頻道。但此時B系統無法掃描到該條訊息,因為事務訊息特殊定製了一個狀態(類似於初始、正常等),只有某種狀態的訊息才可以被正常掃描。當A系統成功將事務訊息傳送到訊息中介軟體後,就回頭執行自己的事務操作,此時有兩種可能,分別為成功和失敗。
如果成功,則向訊息中介軟體傳送請求將之前的事務訊息狀態更改為正常,這樣B系統就可以掃描並獲取到該條訊息,並開始執行自己的本地事務操作。如果成功,則實現了此次兩個資料庫上的事務同時成功;如果失敗,則通過訊息方法傳送到訊息中介軟體另一個頻道,已訂閱該頻道的A系統收到該回滾訊息後,對自己之前提交的事務操作進行回滾。
如果失敗,則不進行任何操作。訊息中介軟體上有自我掃描機制,會定期掃描快取在本地的事務訊息,如果發現某條事務訊息超過指定時間仍為初始狀態,則傳送訊息回查A系統該訊息對應的事務是否成功提交,如果已提交則更新訊息狀態為正常,投遞到B系統;如果沒有成功提交,則清理掉此事務訊息。
從上面的流程可以看出,通過訊息進行事務非同步的方式避免了傳統兩階段提交事務方式對資料長時間的資源鎖定,所以資料庫整體的吞吐率和效能大大超過傳統的兩階段提交方式。
從本質上來說,對比柔性事務解決分散式事務的思路,訊息服務在其中扮演了事務日誌的職能,對全域性事務有一個統一的記錄和排程能力;事務的參與者通過對訊息訂閱關係建立了事務間的關聯。在採用訊息服務實現分散式事務的場景如果出現異常時,一般會採用正向補償的方式,即不會像傳統事務方式出現異常時依次進行回滾,會通過訊息的不斷重試或人工干預的方式讓該事務鏈路繼續朝前執行,而避免出現事務回滾。
支付寶XTS框架
支付寶開發了XTS分散式事務框架,是一套基於BASE思想實現的一套類似2階段提交的分散式事務方案。
XTS是TCC(Try/Confirm/Cancel,預佔/確認/取消)型事務,典型的補償型事務實現。上文已經介紹過,TCC協議包括如下三個階段:
try階段:主要是對業務系統做檢測及資源預留;
confirm階段:主要是對業務系統做確認提交,try階段執行成功並開始執行confirm階段時,預設confirm階段是不會出錯的。也就是說,只要try成功,confirm一定成功。
cancel階段:主要是在業務執行錯誤需要回滾的狀態下,執行業務取消,預留資源釋放。
XTS框架引入了主事務管理器和分支事務管理器兩個角色。其中,主事務管理器用於生成全域性事務id,而分支事務管理器用於和各業務系統互動記錄各子事務提交情況。XTS框架整體運轉流程如下:
業務系統先提交事務給主業務管理器,拿到全域性事務編號txId。之後帶上txId再去呼叫其他參與事務的業務系統,這個txId在整個分散式事務的宣告週期中用於建立主事務和分支事務之前的對應關係。參與事務的業務系統接收到請求後,最好必要的準備工作後將內容記錄到分支事務管理器。
進入事務提交階段後,業務系統向主事務系統提交事務,主事務系統先完成本地事務的提交,後傳送請求給分支事務管理器確認分支事務,分支事務管理器再去呼叫各業務系統執行事務操作並等待返回結果傳送給主事務管理器。如果分支事務全部成功,則該分散式事務成功執行;如果失敗,則進入回滾流程:主事務管理器線回滾本地事務,再依次去取消分支事務。
這裡有一個地方需要注意:該框架的事務回滾和補償機制需要開發人員自行實現,這對於開發工作增添了額外的負擔。為了減消這個負擔,阿里開發了TXC事務平臺:
分散式事務平臺TXC
TXC也是阿里基於兩階段提交理論實現的分散式事務框架,支援分散式資料庫事務、多庫事務、訊息事務、服務鏈路呼叫事務及各種其他事務。和支付寶XTS框架相比,主要區別有兩個:一是主事務和分支事務都是維護在同一臺TXC伺服器上的;二是事務回滾或補償程式碼不需要開發人員編寫,平臺支援自動生成。
相比於傳統的兩階段提交方式,最大的區別在於XA在準備階段是沒有提交本地事務的,而TXC則是立即執行並可見,在隔離性級別上實現的是讀未提交(read uncommitted), 所以避免了在分散式事務中對於資料的長時間鎖佔用。也就是說,Txc在允許資料髒讀的業務場景中,能充分發揮效能上的優勢。比如商品在大促秒殺場景下,允許商品的庫存在事務沒有提交前給前端應用提供查詢,只不過在最後訂單扣減庫存時進行控制,避免商品超賣現場的發生。如果業務場景不允許資料的髒讀,TXC平臺也支援select
for update以及提供@hint的功能臨時提升事務的隔離級別。
同時TXC伺服器端會記錄當前處理事務對資料庫中進行了修改資料的資訊(行資訊),當有其他事務也要對這些資料進行修改操作時,TXC服務端會協調兩個事務間的執行,避免在第一個事務沒有提交前,同樣的資料會被另一個事務對資料進行修改。從本質來說,將原來傳統事務場景下,由資料庫提供的鎖機制提升到了Txc服務端進行了實現,這樣相比於資料庫鎖的實現成本更加輕量, Txc本身服務能力的擴充套件能力,最終在同樣實現事務隔離性的前提下,大大提升了整體的資料庫處理吞吐率。
這裡再主要說說TXC如何實現的事務自動回滾。
1 )使用者在向TXC伺服器發起事務請求後,進入到資料庫的操作時,會對該分支事務在TXC伺服器上進行註冊。當資源管理器捕捉到SQL的請求後,會對
SQL語句進行SQL解析,如果是執行Insert/Delete/Update的SQL操作,則會針對該SQL語句構造出對應的SQL查詢語句,將當前SQL請求要修改的資料先從資料庫中獲取,以undo日誌的方式儲存起來,用於將來回滾;
2 )進行實際的SQL語句的執行,在SQL執行完畢以後,會再次通過查詢方式獲取到修改後的資料,並儲存為redo日誌,用於業務回滾前髒資料的校驗。
3 )當SQL的執行和undo/redo日誌作為一個本地事務提交給資料庫的同時,也會更新分支事務狀態。當整個事務成功提交後,則會刪除undo/redo曰志。
當出現事務回滾時,會按以下順序進行資料的恢復和操作。首先對比當資料庫中資料值與之前儲存的redo日誌中被修改的值是否一致,如果一致則根據undo曰志生成回滾用的undo SQL並執行,恢復資料到執行事務前的狀態;如果當前資料庫中的資料與redo日誌中的值不一致,則說明是該分散式事 務在第一階段修改了資料後,又被其他執行緒(可能是通過非TXC事務控制的資料訪問渠道)修改了該資料,這樣就不能再繼續進行資料的自動回滾,否則會出現業務不一致的情況,回滾會丟擲異常,由TXC Server發出告警,引入人工干預。