本篇文章咱們重點討論關於分佈式事務的一些相關知識點。這是學習分佈式系統的一個必不可少的技術。咱們最多見的案例就是銀行轉帳問題,A帳戶向B帳戶轉100元,那麼A帳戶餘額要減小100,B帳戶上要增長100。兩個步驟必須都要成功纔算成功,只成功一個的話應當回滾掉。若是A和B不在同一個環境或者系統上,這個事務就是分佈式事務了,那麼在這種狀況下,如何保證事務的正確執行,有哪些執行方案呢?html
在將分佈式事務的開始,咱們先從幾個分佈式系統方面的基礎知識點提及,首先是大名鼎鼎的CAP理論,CAP三個字母表明瞭三個單詞,Consistency(一致性),Availability(可用性),Partition Tolerance(分區容忍性)。CAP理論指的是任何一個分佈式系統,最多隻能保證CAP三項中的兩項。算法
首先,一致性,它表示全部的數據節點上的數據要保證是一致且正確的。可用性指的是每個操做老是可以在必定時間內返回結果便是說每一個讀寫操做都是成功的。咱們平時常常看到的一些對於系統穩定性的描述都是達到了幾個9,好比3個9就是99.9%,4個9就是99.99%。這就意味着系統只有在極少數狀況下才會發生故障或錯誤。分區容忍性指的是當出現部分節點故障時,分佈式系統仍然可以正常運行。數據庫
常見的分佈式系統應用架構都是AP或者CP,由於若是沒有P那本質上就是個單機應用談不上分佈式了。工做中最經常使用到的幾個註冊中心,ZooKeeper就是基於CP架構的,Eureka是基於AP的。編程
在工程實踐中,基於CAP理論又演化出一個新的理論-BASE理論。Base表明了三個單詞:Basically Available(基本可用),Soft State(軟狀態)和Eventally Consistent(最終一致性)。它的核心思想是最終一致性,即沒法作到強一致性,但咱們能夠根據實際業務狀況,使系統達到最終一致性。服務器
首先,Base理論中的基本可用,就是不追求CAP中的任什麼時候候的讀寫操做都是成功的,但系統可以保證基本運行。好比咱們平時雙十一的時候可能會出現排隊或者失敗的狀況,其實就是犧牲了部分可用性來保證系統的穩定。網絡
軟狀態能夠對標ACID中的強一致性,ACID中要麼全作要麼全不作,全部用戶看到的數據都是絕對一致的。但軟狀態是容許出現數據不一致的時刻,至關因而一箇中間狀態,幾個數據節點的數據出現了延遲的狀況。架構
最終一致性指的是數據不能一直處於軟狀態的狀況,最終仍是要達到全部節點數據一致的。這也是咱們大部分開發過程當中所追求的。框架
數據一致性模型通常分爲弱一致性和強一致性,上述的BASE理論是實現的最終一致性其實就是弱一致性,而強一致性有時候也稱爲線性一致性,就是每次更新操做後,其餘每一個進程獲取到的數據都是最新的,這種方式對用戶友好,即用戶以前作了什麼操做,下一步就能保證獲得什麼。但這種方式會犧牲系統的可用性,能夠理解爲咱們實現了CP犧牲了A。異步
除了強一致性以外的一致性模型都是弱一致性,也就是說系統不承諾更新操做後必定能保證下次能讀取到最新數據,要能正確讀取到這個數據須要等待一段時間,這個時間差稱做「不一致窗口」。分佈式
最終一致性是弱一致性的特例,它強調的是全部節點的數據副本,通過一段時間後,必定能達到所有一致。它的不一致窗口的時間主要受通訊延遲,系統負載和複製副本的個數影響。其根據不一樣的保證也能夠分爲不一樣模型,包括因果一致性和會話一致性等。
因果一致性要求有因果關係的操做順序獲得保證,非因果關係的操做順序則無所謂。會話一致性將對系統數據的訪問過程框定在了一個會話當中,約定了系統能保證在同一個有效的會話中實現「讀己之所寫」的一致性,就是在你的一次訪問中,執行更新操做以後,客戶端可以在同一個會話中始終讀取到該數據項的最新值。
那麼爲了實現數據一致性,咱們就天然要回到咱們一開始就要說的分佈式事務的概念了,它不一樣於咱們平時單機應用上的服務,因爲在數據分佈在多臺服務器上,咱們就不能用傳統的方式來保證事務的正確提交,這就引出了集中針對分佈式事務的解決方案。
兩階段提交(2PC,Two-phase Commit Protocol)是很是經典的強一致性、中心化的原子提交協議。這個算法包含了兩類角色,協調者(Coordinator)和參與者(Participants)。所謂的兩階段指的是準備階段(Commit-request)和執行階段(Commit)。
在準備階段的時候,協調者通知各個參與者準備提交事務,詢問它們是否接受。參與者們會各自反饋本身的響應,贊成或者取消(故障),在這個階段裏面,全部參與者都沒有進行commit事務操做。
協調者收到參與者們的反饋結果後,進行決策,若是全部的參與者都表示贊成,則要提交事務,不然就是取消。此時通知全部參與者們進行事務提交/取消,參與者們接受到協調者的通知後進行事務操做commit/rollback。
可是兩階段提交存在着一些問題,列舉以下:
由於講了二階段提交,那麼這裏也順便提一下咱們被問到MySQL時常常會說的XA規範,由於它其實是實現了二階段提交的。它是由 X/Open 組織提出的分佈式事務規範,XA 規範主要定義了事務協調者(Transaction Manager)和資源管理器(Resource Manager)之間的接口。
事務管理器擔任着協調者的角色,它來負責協調和管理事務,提供給AP應用程序編程接口並管理資源管理器。事務管理器向事務指定標識,監視它們的進程,並負責處理事務的完成和失敗。資源管理器,能夠理解爲一個DBMS系統,或者消息服務器管理系統。應用程序經過資源管理器對資源進行控制,資源必須實現XA定義的接口。資源管理器提供了存儲共享資源的支持。
目前,主流數據庫都提供了對 XA 的支持,在 JMS 規範中,即 Java 消息服務(Java Message Service)中,也基於 XA 定義了對事務的支持。
根據2PC的規範,XA也是將事務分爲兩個步驟準備(Prepare)和提交(Commit)階段。準備階段就是TM向RM發送Prepare命令,準備提交,RM執行數據操做後,返回TM結果。TM根據收到的結果,進入提交階段,通知RM進行Commit或者Rollback操做。
在MySQL中,有兩種XA事務,一種是內部XA,一種是外部XA。
在 MySQL 的 InnoDB 存儲引擎中,開啓 binlog 的狀況下,MySQL 會同時維護 binlog 日誌與 InnoDB 的 redo log,爲了保證這兩個日誌的一致性,MySQL 使用了 XA 事務,因爲是在 MySQL 單機上工做,因此被稱爲內部 XA。內部 XA 事務由 binlog 做爲協調者,在事務提交時,則須要將提交信息寫入二進制日誌,也就是說,binlog 的參與者是 MySQL 自己。
外部 XA 就是典型的分佈式事務,MySQL 支持 XA START/END/PREPARE/Commit 這些 SQL 語句,經過使用這些命令,能夠完成分佈式事務。
三階段提交(3PC,Three-phase commit)是基於2PC的改進版本,它引入了兩個改動點。
它的三個階段叫作Can Commit, PreCommit和Do Commit。
和2PC的準備階段很像,就是協調者向參與者發起CanCommit 請求,參與者返回yes或no。
協調者根據參與者的反應狀況來決定是否能夠繼續事務的 PreCommit 操做。根據響應狀況,有如下兩種可能。
所有返回ok
部分返回ok
此階段進行真正的提交,跟2PC的最終階段有點類似。若是協調者上一步收到了全部的ACK則會通知參與者進行提交操做,參與者收到後進行提交併反饋協調者ACK。但若是協調者上一步未收到ACK響應,一樣也要執行中斷事務。若是超過超時時間參與者都沒有收到協調者的通知,則自動進行Commit。
很顯然3PC因爲協調者和參與者都有了超時機制(2PC只有協調者有),能夠保證資源不會由於協調者的故障而一直鎖定,而且因爲多了一個PreCommit階段,使得參與者在提交以前的狀態是一致的。但這並不能解決最終可能出現的一致性問題,由於在上面DoCommit階段也有介紹,若是參與者未收到協調者的通知,達到了超時時間後,依然會進行提交,這就出現了數據的不一致性。
TCC(Try-Confirm-Cancel)的概念來源於 Pat Helland 發表的一篇名爲「Life beyond Distributed Transactions:an Apostate’s Opinion」的論文。它的三個字母也對應了三個階段:
Try階段失敗能夠Cancel,但Confirm/Cancel階段沒有,爲了解決此問題,TCC中添加了事務日誌,若是Confirm或者Cancle階段出錯,是容許進行重試的,因此這兩個接口須要支持冪等,若是重試依然失敗那就要靠人工介入了。
經過上面的描述,很顯然,TCC相對於以前的2PC等方式,它關注的是業務層而不是數據庫或者存儲資源層面的事務。它的核心思想是針對每一個業務操做,都要添加相應的確認和補償操做,同時把相關的處理從數據庫拿到了業務層,以此實現了跨數據庫的事務。
但正由於它是在業務層進行事務的處理,所以對微服務的侵入性強,業務邏輯的每一個分支都要實現Try,Confirm,Cancel三個操做,並且Confirm,Cancel還要實現冪等。另外TCC 的事務管理器要記錄事務日誌,也會損耗必定的性能。
在業務中引入 TCC 通常是依賴單獨的 TCC 事務框架,最多見的就是Seata框架了,這個能夠本身嘗試去使用體驗下。
在實際生產工做中,咱們還常常會用到一種方案,基於消息補償的方式,它是一種異步事務機制。常見的實現方案有本地消息表、消息隊列等。
首先說下本地消息表,它最初是由 ebay 的工程師提出,核心思想是將分佈式事務拆分紅本地事務進行處理,經過消息日誌的方式來異步執行。因此其實就是利用了各系統本地的事務實現了分佈式事務。在本地要建立一張消息表,業務執行的時候也要往這個消息表裏面存放一條數據,這樣能夠保證存放消息和業務數據都是同時成功或失敗的。成功存放後再進行後續的業務操做,若是成功了則將消息狀態更新爲成功。若是失敗了,首先會有個定時任務常常去掃描未成功的任務去執行,若是失敗也是能夠重試的,因此要保證業務處理接口的冪等。
因此能看出本地消息表的方式是能保證最終一致性的,但可能出現部分時間的數據不一致。
咱們常見的消息隊列中RocketMQ就支持消息事務。因此我這裏講下RocketMQ實現分佈式事務。這部分我從阿里雲的文檔上找的,感興趣的能夠本身去看,顯示幾個概念:
交互流程以下
事務消息發送步驟以下:
事務消息回查步驟以下:
和上面的可靠消息隊列方式相似的,還有一種方案叫作盡最大努力通知。它的核心思想是發起通知方經過必定的機制最大努力將業務處理結果通知到接收方。通常也是經過MQ去實現的。
它和基於可靠消息一致的區別以下(摘自某博客):
一、解決方案思想不一樣
可靠消息一致性,發起通知方須要保證將消息發出去,而且將消息發到接收通知方,消息的可靠性關鍵由發起通知方來保證。 最大努力通知,發起通知方盡最大的努力將業務處理結果通知爲接收通知方,可是可能消息接收不到,此時須要接收通知方主動調用發起通知方的接口查詢業務處理結果,通知的可靠性關鍵在接收通知方。
二、二者的業務應用場景不一樣
可靠消息一致性關注的是交易過程的事務一致,以異步的方式完成交易。 最大努力通知關注的是交易後的通知事務,即將交易結果可靠的通知出去。
三、技術解決方向不一樣
可靠消息一致性要解決消息從發出到接收的一致性,即消息發出而且被接收到。 最大努力通知沒法保證消息從發出到接收的一致性,只提供消息接收的可靠性機制。可靠機制是,最大努力的將消息通知給接收方,當消息沒法被接收方接收時,由接收方主動查詢消費(業務處理結果)。
2PC和3PC都是一種強一致性事務,基於數據庫層面,但也存在一些數據不一致的風險。TCC是一種補償性的事務思想,因爲須要在業務層實現,因此對業務侵入大。基於消息補償的方式,TCC還有盡最大努力通知其實都是一種柔性事務,都是保證了最終一致性,容許出現部分時刻數據不一致的狀況。
這篇文章有點水,其實就是講了點概念,不少仍是摘錄自其餘博客和拉勾教育-分佈式技術原理與實戰45講這門課程的,就當個讀書筆記看吧,至少擴展了一些對分佈式事務的基礎概念的瞭解。