分布式事务 -- seata框架AT模式实现原理
生活随笔
收集整理的這篇文章主要介紹了
分布式事务 -- seata框架AT模式实现原理
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Seata AT 模式
- 上一節中我們提到AT模式是基于XA事務模型演變過來的,所以他的整體機制也是一個改進版本的兩階段提交協議。
- 第一階段:業務數據和回滾日志記錄在同一個本地事務中提交,釋放本地鎖和鏈接資源
- 第二階段:提交異步化,非常快速完成。回滾通過第一階段的回滾日志進行反向補償
- 接下來會解析整個執行流程中每一個階段的具體實現 原來。同時為了更好的理解AT模式的工作機制,我們以產品表za_product來描述整個流程,表結構如下:
AT模式第一階段
- 業務流程中執行庫存扣減操作的數據庫操作時候,Seata會基于數據源代理對原執行的SQL進行解析,代理的配置代碼如下。
-
然后將業務數據在更新前后保存到undo_log 日志表中,利用本地事務的ACID特性,把業務數據的更新和回滾日志寫入同一個本地事務中進行提交,完整的執行流程如圖
-
日志表結構如下:
- 假設AT分支事務的業務邏輯是如下SQl:
- 如上則第一階段的執行邏輯如下
- 通過DataSourceProxy對業務SQL進行解析,得到SQl的類型(UPDATE),表(za_product),條件(where product_code = “GP202103221029”)等相關信息
- 查詢修改之前的數據鏡像,根據解析得到條件信息生成查詢語句,定位數據
- 如上SQl得到產品的對應庫存數據1000
- 執行業務SQL:更新這條記錄的count= count-1
- 查詢修改之后的數據鏡像,根據鏡像杰哥通過主鍵定位數據
- 得到修改之后的鏡像數據,此時count= 999
- 插入回滾日志:把前,后鏡像數據以及業務SQl相關的信息組成一條回滾日志記錄,插入undo_log表中,可以在對應數據庫的undo_log表中獲得數據,如下圖:
- rollback_info字段是一個文件類型,包含了回滾的數據beforeImage和afterImage,使用官網數據,如下所示:
- 提交前,向TC注冊分支事務:申請za_product表中主機值等于1 的記錄的全局鎖
- 本地事務提交:業務數據的更新和簽名步驟中生成的undo_log一起提交
- 將本地事務提交的結果上報給TC
- 從AT的第一階段實現原理看,分支的本地事務可以在第一階段提交完成后馬上釋放本地事務所定的資源,這個是AT事務和XA最大的不同點,XA事務的兩階段提交,一般鎖定資源后持續到第二階段的提交或者回滾后才釋放資源,所以實際上 AT模式降低了鎖定的范圍 ,從而大幅度提升了分布式事務處理的效率,之所以這樣實現,是因為Seata記錄了回滾日志,即使第二階段發生異常,只需要更具undo_log記錄的數據進行回滾即可。
AT模式第二階段
- TC接受到所有事務分支的事務狀態匯報后,決定對全局事務進行提交或者回滾。
事務提交
- 如果決定是全局提交,說明此事所有分支事務已經完成了提交,只需要清理undo_log日志即可,這也是和XA最大的不同點,因為在第一階段各個分支的本地事務就已經提交了,所以這里并不需要TC來觸發所有分支事務的提交,如下圖:
- 如上圖中事務提交流程:
- 分支事務收到TC的提交請求后吧請求放入一個異步隊列,并馬上返回提交成功的結果給TC
- 從異步隊列中執行分支,提交請求,批量刪除對應的undo_log日志。
- 第一個步驟中TC并不需要同步知道分支事務的處理結果,所以分支事務才會采用異步的方式來執行,因為對于提交操作來說,分支事務只需要清理undo_log中的日志即可,而即使清理失敗,也不會對整個分布式事務產生任何影響。
事務回滾
- 整個全局事務鏈中,任何一個事務分支執行失敗,全局事務都會進入事務回滾流程。此處回滾根據undo_log中記錄的鏡像數據進行補償,如,回滾成功,則數據的一致性得到保持。如下流程:
- 所有分支事務接受到TC的事務回滾請求后,分支事務參與者開啟一個本地事務,執行流程如下:
- 通過XID和branch ID查找到相應的undo_log記錄
- 數據校驗:拿undo_log中afterImage鏡像數據與當前業務表中數據鏡像比較(樂觀鎖),如果不同,說明數據被當前全局事務之外的動作修改了,那么事務不會回滾。
- 如果afterImage中數據和當前業務表中對應的數據相同,則根據undo_log中的beforeImage鏡像數據和業務SQL的相關信息生成回滾語句并執行:
- 提交本地事務,并把本地事務的執行結果(即分支事務回滾的結果)提交給TC。
關于事務隔離性保證
- ACID在事務特種,有一個隔離性,指多個用戶并發的訪問數據庫的時候,數據庫為每一個用戶開啟的事務不能被其他事務的操作所干擾,多個并發事務之間要相互隔離。
- 在AT模式中,當多個全局事務操作同一張表的時候,他的事務隔離的保證是基于全局鎖來實現的,那么我們針對讀隔離,寫隔離進行說明
寫隔離
-
寫隔離是為了在多個全局事務針對同一個表的同一個字段進行更新操作的時候,避免全局事務在沒有被提交之前被其他全局事務修改。寫隔離的主要實現是,在第一階段本地事務提交之前,確保拿到了全局鎖。如果拿不到全局鎖,則不能提交本地事務。并且獲取全局鎖的嘗試會有一個范圍控制,如果超出范圍將會放棄全局鎖的獲取,并且回滾事務,釋放本地鎖。
-
我們用實際案例解析,假設兩個全局事務tx1,tx2,分別對za_product表的count字段進行更新操作,count原始值100.
-
tx1先執行,開啟本地事務,拿到本地鎖(數據庫級別的鎖),更新count= count-1= 99。本地事務提交之前,需要拿到該記錄的全局鎖,然后提交本地事務并釋放本地鎖。
-
tx2后執行,同樣先開啟本地事務,拿到本地鎖,更新count=count-1= 98。本地事務提交之前,嘗試獲取該記錄的全局鎖(全局鎖由TC控制),由于該全局鎖已經被tx1獲取,所以tx2需要等待以重新獲取全局鎖。如果全局事務整體提交,那么提交時序圖如下:
- 如果tx1在第二階段執行全局回滾,那么tx1需要重新獲得該數據的本地鎖,讓后根據undo_log進行事務回滾。此時,如果tx2仍然在等待該激勵的全局鎖,同時持有本地鎖,那么tx1 分支事務的回滾會失敗。tx1分支事務回滾過程會一直重試,直到tx2的全局鎖獲取超時,放棄全局鎖并回滾本地事務,釋放本地鎖,之后tx1的分支事務才會回滾成功。而在整個過程中,全局鎖在tx1 結束之前一直被tx1 持有,所以不會發生臟寫問題。全局事務回滾時序圖如下:
讀隔離
- 我們在數據庫學習中指定有4種隔離級別:
- Read Uncommitted:讀取為提交內容
- Read Committed:讀取已經提交內容
- Repeatable read:可重復讀
- Serializable:可串行化
- 在數據庫本地事務隔離級別為Read Committed或者以上時候,Seata AT事務模式的默認全局隔離級別是Read Uncommitted,在該隔離級別,所有事務都可以看到其他未提交事務的執行結果,產生臟讀。這在最終一致性事務模型中是允許存在的,并且在大部分分布式事務場景中可以接受臟讀。
- 在某些特定場景中要求事務隔離級別必須Read Committed,目前Seata是通過SelectForUpdateExecutor執行器對SELECT FOR UPDATE語句進行代理的,SELECT FOR UPDATE 語句在執行時候回申請全局鎖。如果全局鎖已經被其他分支事務持有,則釋放本地鎖(回滾SELECT FOR UPDATE 語句的本地執行)并重試。在這個過程中,查詢請求會被“BLOCKING”,直到全局鎖被拿到,也就是讀取的相關數據已經提交時候才返回。
上一篇:分布式事務框架seata
總結
以上是生活随笔為你收集整理的分布式事务 -- seata框架AT模式实现原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iphone低电量模式在哪里设置
- 下一篇: 数据结构与算法--代码完整性案例分析