AT 這種事務模式是阿里開源的seata主推的事務模式,本文會詳解AT的原理,並將它與XA模式進行比較
原理
AT 從原理上面看,與 XA 的設計有很多相近之處。XA 是資料庫層面實現的二階段提交, AT 則是應用/驅動層實現的二階段提交。建議您瞭解了XA相關的知識後,來閱讀這篇文章,這樣能夠更快更好的掌握 AT 的原理與設計。
AT的角色和XA一樣分為3個,但是起了不一樣的名稱,大家注意分辨:
- RM 資源管理器,是業務服務,負責本地資料庫的管理,與XA中的RM一致
- TC 事務協調器,是Seata伺服器,負責全域性事務的狀態管理,負責協調各個事務分支的執行,相當於XA中的TM
- TM 事務管理器,是業務服務,負責全域性事務的發起,相當於XA中的APP
AT 的第一階段為prepare,它在這一階段會完成以下事情:
- RM 側,使用者開啟本地事務
RM 側,使用者每進行一次業務資料修改,假設是一個update語句,那麼 AT 會做以下內容:
- 根據update的條件,查詢出修改前的資料,該資料稱為BeforeImage
- 執行update語句,根據BeforeImage中的主鍵,查詢出修改後的資料,該資料稱為AfterImage
- 將BeforeImage和AfterImage儲存到一張undolog表
- 將BeforeImage中的主鍵以及表名,該資料稱為lockKey,記錄下來,留待後續使用
RM 側,使用者提交本地事務時,AT 會做以下內容:
- 將2.4中記錄的所有的lockKey,註冊到 TC(即事務管理器seata)上
- 3.1中的註冊處理會檢查 TC 中,是否已存在衝突的主鍵+表名,如果有衝突,那麼AT會睡眠等待後重試,沒有衝突則儲存
- 3.1成功完成後,提交本地事務
如果 AT 的第一階段所有分支都沒有錯誤,那麼會進行第二階段的commit,AT 會做以下內容:
- TC 會將當前這個全域性事務所有相關的lockKey刪除
- TC 通知與當前這個全域性事務相關的所有業務服務,告知全域性事務已成功,可以刪除undolog中儲存的資料
- RM 收到通知後,刪除undolog中的資料
如果 AT 的第一階段有分支出錯,那麼會進行第二階段的rollback,AT 會做以下內容:
- TC 通知與當前這個全域性事務相關的所有業務服務,告知全域性事務失敗,執行回滾
RM 收到通知後,對本地資料的修改進行回滾,回滾原理如下:
- 從undolog中取出修改前後的BeforeImage和AfterImage
- 如果AfterImage與資料庫中的當前記錄校驗一致,那麼使用BeforeImage中的資料覆蓋當前記錄
- 如果AfterImage與資料庫中的當前記錄不一致,那麼這個時候發生了髒回滾,此時需要人工介入解決
- TC 待全域性事務所有的分支,都完成了回滾,TC 將此全域性事務所有的lockKey刪除
問題分析
AT 模式的一個突出問題是rollback中2.3的髒回滾難以避免。以下步驟能夠觸發該髒回滾:
- 全域性事務g1對資料行A1進行修改 v1 -> v2
- 另一個服務將對資料行A1進行修改 v2 -> v3
- 全域性事務g1回滾,發現資料行A1的當前資料為v3,不等於AfterImage中的v2,回滾失敗
這個髒回滾一旦發生,那麼分散式事務框架沒有辦法保證資料的一致性了,必須要人工介入處理。想要避免髒回滾,需要把所有對這個表的寫訪問,都加上特殊處理(在Seata的Java客戶端中,需要加上GlobalLock註解)。這種約束對於一個上了一定規模的複雜系統,是非常難以保證的。
AT vs XA
上述髒回滾問題,在 XA 事務中不會出現,因為 XA 事務是在資料庫層面實現的,當另一個服務對為資料行A1進行修改時,會因為行鎖被阻塞,與普通事務的表現完全一樣,不會產生問題。
另外 XA 不會發生髒讀,而 AT 會發生髒讀,考慮AT下的如下執行步驟:
- 全域性事務g1對資料行A1進行修改 v1 -> v2
- 另一個服務將讀取資料行A1,獲得資料 v2
- 全域性事務g1回滾,將資料行A1改回 v2 -> v1
這裡面步驟2讀取的資料是v2,是一箇中間態資料。在Seata的手冊中,雖然也有一些方法能夠避免AT模式下,但是涉及到註解和sql改寫,並不優雅。而在XA模式下,由於還沒有進行xa commit,那麼步驟2根據MVCC
讀取到的資料依然是v1,沒有AT模式中的髒讀的困擾。
效能分析
從原理的詳細步驟看,XA事務的效能高於AT,分析如下:
AT 模式下,RM側,上述原理過程中,執行的SQL如下:
- 開啟事務
- 查詢BeforeImage資料
- 執行update
- 查詢AfterImage資料
- 將BeforeImage,AfterImage插入到undolog中
- 提交事務
- 事務完成後,刪除BeforeImage和AfterImage
而 XA 模式下,RM側,執行的SQL如下:
- xa begin
- 執行update
- xa end
- xa prepare
- xa commit
兩者對比,相關的開啟/提交事務是兩個模式都需要的,效能差異不大。但是從執行的DML操作來看,AT 下的 SQL 數量為:3 writes,2 read,比 XA 下僅一個update多出許多,因此在效能上會有較大的差距
從上述理論分析,XA 事務效能會大幅高於AT,應當可以在postgres資料庫上驗證出來;而mysql資料庫,在當前的5.8版本上,由於xa prepare後,需要將當前連線斷開才能夠在其他連線上xa commit,所以會有一個重新建立連線的開銷,最終效能對比參考下一節。
效能實測
上述進行了理論上的效能分析,我同時也做了效能實測,詳細的測試過程和結果資料,參考 xa-at bench
dtm實現的XA事務,為了在極端情況下,也能保證XA事務能夠正確的被清理,會在業務事務中對子事務屏障表進行插入,因此會比上述理論分析中,多一個sql寫入。
我們可以看到,最終的結果XA效能優於AT。如果未來Mysql完善了XA的實現,可以不用關閉當前連線也能夠允許其他連線提交xa事務,那麼XA的效能還能夠提升一大截。
AT的意義
mysql在版本5.6中,xa相關API存在bug。如果當前連線在xa prepare之後,連線斷開,那麼這個連線未完成的事務會被自動回滾。這樣的bug導致mysql的XA模式是無法保證正確性的,在各種應用crash中,可能導致資料不一致。因此AT在mysql的5.6版本及更低版本使用中,是具有很高應用價值的。
另外部分大廠的資料庫是禁止使用XA事務的,這種特定場景下,選型AT模式,也是合理的。
對於其他場景,建議優先考慮 XA 事務。
小結
作者對AT模式的完整實現原始碼,並未完整閱讀。上述的相關原理是根據自己閱讀相關資料,並參考了seata-golang的原始碼而寫。文中如果不準確之處,希望各位讀者幫忙指正
歡迎訪問 https://github.com/dtm-labs/dtm 並star支援我們