一. 簡介
1. 什么是本地事務?
基於關系型數據庫的事務,叫做本地事務,也叫做數據庫事務。 本地事務通常是應用和數據庫在一個服務器上,利用數據庫本身的事務特性,從而實現本地事務。
數據庫事務的特性:ACID。
(1). 原子性(Atomicity):指一個事務內的所有操作要么都執行,要么都不執行。
(2). 一致性(Consistency):指數據是滿足完整性約束的,也就是不會存在中間狀態的數據。
(3). 隔離性(Isolation),指的是多個事務並發執行的時候不會互相干擾,即一個事務內部的數據對於其他事務來說是隔離的。
(4). 持久性(Durability),指的是一個事務完成了之后數據就被永遠保存下來,之后的其他操作或故障都不會對事務的結果產生影響。
PS: Redis中的事務不支持回滾,這是一個特殊情況。 下面是官網解釋,詳見 https://www.cnblogs.com/yaopengfei/p/13922295.html
2. 什么是分布式事務?
(1). 含義:在分布式系統中,不同服務之間需要通過網絡協作來完成的事務,叫做分布式事務。比如下單業務:先創建訂單→扣減庫存。
A. 如果是本地事務:借助數據庫就能完成,比如請求接口POrder,里面的業務如下:
begin transaction; 1. 訪問訂單表,創建訂單 2. 訪問庫存表:扣減庫存 (以上兩個表是同一個DB) commit transation;
B. 如果是分布式事務:存在訂單微服務和庫存微服務,我們請求訂單微服務中POrder接口,該接口除了需要調用自身DB創建訂單外,還需要調用庫存微服務的接口,這就存在網絡通信,傳統的數據庫事務無法滿足。
begin transaction; 1. 訂單微服務,創建訂單 (本地DB) 2. 通過網絡調用庫存微服務中接口,進行扣減庫存 commit transation;
(2). 產生分布式事務的幾種情況
A. 跨進程通信(典型微服務架構,多服務對應多DB)
比如,典型的下單流程: 客戶端請求訂單微服務中的接口,該接口中除了要向訂單DB中插入數據,還要調用庫存微服務中的接口,這就形成分布式事務。
B. 單服務對應多DB
比如,單應用由於數據較多,需要數據庫分庫現象,比如用戶管理系統中存在多個DB,用戶DB、訂單DB、材料DB等等,每個DB都對應一個不同的數據庫連接,也是分布式事務
C. 多服務對應單DB
比如,有些情況下,雖然服務分多個了,但還是1個DB,訂單微服務和庫存微服務都訪問同一個數據庫,下單的時候,同樣道理,存在網絡通信,跨進程,不同的微服務對應不同的DB連接,也是分布式事務。
二. 理論分析
1. CAP理論分析
(分布式事務需要CAP理論支持)
(1). 什么是CAP?
CAP( Consistency、Availability、Partition Tolerance),分別表示一致性、可用性、分區容忍性。
下面用1個增加、查詢商品的流程來解釋什么是CAP。
A. 增加商品,向主數據庫中插入數據。
B. 主數據庫寫入成功,需要把數據同步給從數據庫。
C.查詢商品,訪問從數據庫讀取。
① C-Consistency
一致性是指寫操作后的讀操作可以讀取到最新的數據狀態,當數據分布在多個節點上,從任意結點讀取到的數據都是最新的狀態。
上圖中,商品信息的讀寫要滿足一致性就是要實現如下目標:
-
商品服務寫入主數據庫成功,則向從數據庫查詢新數據也成功。
-
商品服務寫入主數據庫失敗,則向從數據庫查詢新數據也失敗。
如何實現一致性?
-
寫入主數據庫后要將數據同步到從數據庫。
-
寫入主數據庫后,在向從數據庫同步期間要將從數據庫鎖定,待同步完成后再釋放鎖,以免在新數據寫入成功后,向從數據庫查詢到舊的數據。
分布式系統一致性的特點:
-
由於存在數據同步的過程,寫操作的響應會有一定的延遲。
-
為了保證數據一致性會對資源暫時鎖定,待數據同步完成釋放鎖定資源。
-
如果請求數據同步失敗的結點則會返回錯誤信息,一定不會返回舊數據。
② A-Availability
可用性是指任何事務操作都可以立即得到響應結果,且不會出現響應超時或響應錯誤。
上圖中,商品信息讀取滿足可用性就是要實現如下目標:
1. 從數據庫接收到數據查詢的請求則立即能夠響應數據查詢結果。
2. 從數據庫不允許出現響應超時或響應錯誤。
如何實現可用性?
1. 寫入主數據庫后要將數據同步到從數據庫。
2. 由於要保證從數據庫的可用性,不可將從數據庫中的資源進行鎖定。
3. 即使數據還沒有同步過來,從數據庫也要返回要查詢的數據,哪怕是舊數據,如果連舊數據也沒有則可以按照約定返回一個默認信息,但不能返回錯誤或響應超時。
分布式系統可用性的特點:所有請求都有響應,且不會出現響應超時或響應錯誤
③ P-Partition Tolerance
通常分布式系統的各結點部署在不同的子網,這就是網絡分區,不可避免的會出現由於網絡問題而導致結點之間通信失敗,此時仍可對外提供服務,這叫分區容忍性。(P是一定存在的!!!)
上圖中,商品信息讀寫滿足分區容忍性就是要實現如下目標:
1. 主數據庫向從數據庫同步數據失敗不影響讀寫操作。
2. 其一個結點掛掉不影響另一個結點對外提供服務。
如何實現分區容忍性?
1. 盡量使用異步取代同步操作,例如使用異步方式將數據從主數據庫同步到從數據,這樣結點之間能有效的實現松耦合。
2. 添加從數據庫結點,其中一個從結點掛掉其它從結點提供服務。
分布式分區容忍性的特點:分區容忍性分是布式系統具備的基本能力
總結:在所有分布式事務場景中不會同時具備 CAP 三個特性,因為在具備了P的前提下C和A是不能共存的。
(2). CAP常見的組合形式?
① AP
放棄一致性,追求分區容忍性和可用性。這是很多分布式系統設計時的選擇。
例如:上邊的商品管理,完全可以實現 AP,前提是只要用戶可以接受所查詢到的數據在一定時間內不是最新的即可。
通常實現 AP 都會保證最終一致性,后面將的 BASE 理論就是根據 AP 來擴展的,一些業務場景比如:訂單退款,今日退款成功,明日賬戶到賬,只要用戶可以接受在一定的時間內到賬即可。
② CP
放棄可用性,追求一致性和分區容錯性,zookeeper 其實就是追求的強一致,又比如跨行轉賬,一次轉賬請求要等待雙方銀行系統都完成整個事務才算完成。
③ CA
放棄分區容忍性,即不進行分區,不考慮由於網絡不通或結點掛掉的問題,則可以實現一致性和可用性。那么系統將不是一個標准的分布式系統,是一個單體系統。
(3). 總結
CAP 是一個已經被證實的理論,一個分布式系統最多只能同時滿足:一致性(Consistency)、可用性(Availability)和分區容忍性(Partition Tolerance)這三項中的兩項。它可以作為我們進行架構設計、技術選型的考量標准。對於多數大型互聯網應用的場景,結點眾多、部署分散,而且現在的集群規模越來越大,所以節點故障、網絡故障是常態,而且要保證服務可用性達到 N 個 9(99.99..%),並要達到良好的響應性能來提高用戶體驗,因此一般都會做出如下選擇:保證 P 和 A ,舍棄 C 強一致,保證最終一致性。
2. Base理論分析
(1). 背景
CAP 理論告訴我們一個分布式系統最多只能同時滿足一致性(Consistency)、可用性(Availability)和分區容忍性(Partition tolerance)這三項中的兩項,其中AP在實際應用中較多,AP 即舍棄一致性,保證可用性和分區容忍性,但是在實際生產中很多場景都要實現一致性,比如前邊我們舉的例子主數據庫向從數據庫同步數據,即使不要一致性,但是最終也要將數據同步成功來保證數據一致,這種一致性和 CAP 中的一致性不同,CAP 中的一致性要求 在任何時間查詢每個結點數據都必須一致,它強調的是強一致性,Base理論強調的是最終一致性,是允許可以在一段時間內每個結點的數據不一致,但是經過一段時間每個結點的數據必須一致,它強調的是最終數據的一致性。
(2). Base理論
BASE 是 Basically Available(基本可用)、Soft state(軟狀態)和 Eventually consistent (最終一致性)三個短語的縮寫。BASE 理論是對 CAP 中 AP 的一個擴展,通過犧牲強一致性來獲得可用性,當出現故障允許部分不可用但要保證核心功能可用,允許數據在一段時間內是不一致的,但最終達到一致狀態。滿足BASE理論的事務,我們稱之為“柔性事務”。
-
基本可用:分布式系統在出現故障時,允許損失部分可用功能,保證核心功能可用。如電商網站交易付款出現問題了,商品依然可以正常瀏覽。
-
軟狀態:由於不要求強一致性,所以BASE允許系統中存在中間狀態(也叫軟狀態),這個狀態不影響系統可用性,如訂單的"支付中"、“數據同步中”等狀態,待數據最終一致后狀態改為“成功”狀態。
-
最終一致:最終一致是指經過一段時間后,所有節點數據都將會達到一致。如訂單的"支付中"狀態,最終會變 為“支付成功”或者"支付失敗",使訂單狀態與實際交易結果達成一致,但需要一定時間的延遲、等待。
3. 分布式事務解決方案分類
(1). 強一致性
任意時刻數據都是一致的,常見的有:2PC、3PC。
(2). 弱一致性
允許某一時刻不一致,承諾在一定時間內變成一致的,常見的有TCC。
(Try-Confirm-Cancel 代碼層面)
(3). 最終一致性
允許數據不一致,但是最終最終,數據還是得一致的業務層面。 常見的有:本地消息表、最大努力通知。
三. 強一致性-2PC
1. 2PC原理
(1). 含義
2PC(Two-phase commit protocol) 顧名思義,就是分兩階段提交,准備階段(Prepare phase)、提交階段(commit phase),2 是指兩個階段,P 是指准備階段,C 是指提交階段。
(2). 流程
2PC引入一個新的角色,事務協調器,用來協調各個參與者的提交和回滾。
A. 准備階段:協調器給各個參與者發送准備指令,同步阻塞等待所有參與者響應之后進入提交階段。
PS:事務管理器給每個參與者發送 Prepare 消息,每個數據庫參與者在本地執行事務,並寫本地的 Undo/Redo 日志,此時事務沒有提交。(Undo 日志是記錄修改前的數據,用於數據庫回滾,Redo 日志是記錄修改后的數據,用於提交事務后寫入數據文件)
B. 提交階段:①假設所有參與者均返回成功,那么協調器向所有參與者發送提交事務的指令,並阻塞等待所有事務執行成功后返回執行成功,最后釋放鎖資源。
② 假設有1個參數返回失敗,那么協調器則向所有參與者發送回滾事務的指令,並阻塞等待所有事務回滾成功后返回執行完成,最后釋放鎖資源。
成功流程:
失敗流程:
2. 2PC異常剖析
(1). 提交階段失敗
A. 提交事務失敗:只能不斷重試,因為有可能一些參與者的事務已經提交成功了,只能不斷的重試,直到提交成功,到最后真的不行只能人工介入處理。
B. 回滾事務失敗:不斷重試,直到所有參與者都回滾了,不然那些在第一階段准備成功的參與者會一直阻塞着。
總結:2PC 是一個同步阻塞協議,像第一階段協調者會等待所有參與者響應才會進行下一步操作,當然第一階段的協調者有超時機制,假設因為網絡原因沒有收到某參與者的響應或某參與者掛了,那么超時后就會判斷事務失敗,向所有參與者發送回滾命令。在第二階段協調者的沒法超時,因為按照我們上面分析只能不斷重試!
(2). 事務協調器故障
(協調者故障,通過選舉得到新協調者)
A. 假設協調者在發送准備命令之前掛了,還行等於事務還沒開始。
B. 假設協調者在發送准備命令之后掛了,這就不太行了,有些參與者等於都執行了處於事務資源鎖定的狀態。不僅事務執行不下去,還會因為鎖定了一些公共資源而阻塞系統其它操作。
C. 假設協調者在發送回滾事務命令之前掛了,那么事務也是執行不下去,且在第一階段那些准備成功參與者都阻塞着。
D. 假設協調者在發送回滾事務命令之后掛了,這個還行,至少命令發出去了,很大的概率都會回滾成功,資源都會釋放。但是如果出現網絡分區問題,某些參與者將因為收不到命令而阻塞着。
E. 假設協調者在發送提交事務命令之前掛了,這個不行,傻了!這下是所有資源都阻塞着。
F. 假設協調者在發送提交事務命令之后掛了,這個還行,也是至少命令發出去了,很大概率都會提交成功,然后釋放資源,但是如果出現網絡分區問題某些參與者將因為收不到命令而阻塞着。
3. XA方案
參考:https://www.cnblogs.com/dyzcs/p/13780668.html
4. .Net下的DTC模式
EF支持,EFCore不支持,EF Core中的 System.Transactions 實現將不包括對分布式事務的支持,因此不能使用 TransactionScope 或 CommittableTransaction 來跨多個資源管理器協調事務。主要分布式事務需要依賴於 Windows 系統的 MSDTC 服務,但.NET Core要實現跨平台,基於跨平台的分布式事務沒有統一的標准,后續版希望改進。
詳見:https://www.cnblogs.com/yaopengfei/p/7748221.html 底部。
開啟msdtc服務的步驟: cmd命令→net start msdtc
主要依賴下面這個服務:
5. Seata方案(Java)
參考:https://www.cnblogs.com/dyzcs/p/13780668.html
Seata實現2PC與傳統2PC的差別
架構層次方面:傳統 2PC 方案的 RM 實際上是在數據庫層,RM 本質上就是數據庫自身,通過 XA 協議實現,而 Seata 的 RM 是以 jar 包的形式作為中間件層部署在應用程序這一側的。
兩階段提交方面:傳統 2PC無論第二階段的決議是 commit 還是 rollback ,事務性資源的鎖都要保持到 Phase2 完成才釋放。而 Seata 的做法是在 Phase1 就將本地事務提交,這樣就可以省去 Phase2 持鎖的時間,整體提高效率。
6. 總結
(1). 2PC 是一種盡量保證強一致性的分布式事務,因此它是同步阻塞的,而同步阻塞就導致長久的資源鎖定問題,總體而言效率低,並且存在單點故障(協調器)問題,在極端條件下存在數據不一致的風險。
(2). 2PC 適用於數據庫層面的分布式事務場景,而我們業務需求有時候不僅僅關乎數據庫,也有可能是上傳一張圖片或者發送一條短信。
四. 強一致性-3PC
1. 3PC含義與流程
3PC 包含了三個階段:分別是准備階段(CanCommit)、預提交階段(PreCommit)和提交階段(DoCommit)。相比於 2PC 它在參與者中也引入了超時機制,並且新增了一個階段使得參與者可以利用這一個階段統一各自的狀態。
PS:3PC的預提交階段等價於2PC的准備階段,3PC的提交階段等價於2PC的提交階段,3PC的准備階段是新增的。
(1). 准備階段
准備階段的變更成不會直接執行事務,而是會先去詢問此時的參與者是否有條件接這個事務,因此不會一來就干活直接鎖資源,使得在某些資源不可用的情況下所有參與者都阻塞着。
(2). 預提交階段
預提交階段的引入起到了一個統一狀態的作用,它像一道柵欄,表明在預提交階段前所有參與者其實還未都回應,在預提交階段中表明所有參與者都已經回應了。
(3). 提交階段
提交事務或者回滾事務。
2. 3PC剖析
(1). 上面我們知道,2PC 是同步阻塞的,我們已經分析了協調者掛在了提交請求還未發出去的時候是最傷的,所有參與者都已經鎖定資源並且阻塞等待着。
那么3PC中引入了超時機制,參與者就不會傻等了,如果是等待提交命令超時,那么參與者就會提交事務了,因為都到了這一階段了大概率是提交的,如果是等待預提交命令超時,那該干啥就干啥了,反正本來啥也沒干。
然而超時機制也會帶來數據不一致的問題,比如在等待提交命令時候超時了,參與者默認執行的是提交事務操作,但是有可能執行的是回滾操作,這樣一來數據就不一致了。
(2). 3PC 的引入是為了解決提交階段 2PC 協調者和某參與者都掛了之后新選舉的協調者不知道當前應該提交還是回滾的問題。
新協調者來的時候發現有一個參與者處於預提交或者提交階段,那么表明已經經過了所有參與者的確認了,所以此時執行的就是提交命令。
所以說 3PC 就是通過引入預提交階段來使得參與者之間的狀態得到統一,也就是留了一個階段讓大家同步一下。
但是這也只能讓協調者知道該如果做,但不能保證這樣做一定對,這其實和上面 2PC 分析一致,因為掛了的參與者到底有沒有執行事務無法斷定。
所以說 3PC 通過預提交階段可以減少故障恢復時候的復雜性,但是不能保證數據一致,除非掛了的那個參與者恢復。
3. 總結
3PC引入了參與者超時機制,並且增加了預提交階段使得故障恢復之后協調者的決策復雜度降低,但整體的交互過程更長了,性能有所下降,並且還是會存在數據不一致問題。所以 2PC 和 3PC 都不能保證數據100%一致,因此一般都需要有定時掃描補償機制
參考:https://www.cnblogs.com/dyzcs/p/13780668.html
https://zhuanlan.zhihu.com/p/183753774
!
- 作 者 : Yaopengfei(姚鵬飛)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 聲 明1 : 如有錯誤,歡迎討論,請勿謾罵^_^。
- 聲 明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。