分布式事務原理與實踐
所謂事務,它是一個操作集合,這些操作要麽都執行,要麽都不執行,它是一個不可分割的工作單位。比如網上訂票,要麽你定票成功,余票減一張; 要麽你定票失敗,余票的數量不變。這就要求購票和余票減少這兩個不同的操作必須放在一起,成為一個完整的邏輯鏈,這樣就構成了一個事務。
事務說起來很簡單,就是BEGIN TRAN,COMMIT TRAN,ROLLBACK TRAN 三個語句,但是事務的內部實現原理是非常復雜的,而分布式事務由於需要跨越多個服務器,操作多個數據庫,復雜度比單機事務要復雜的多,本文嘗試通過鎖和並發控制的講解,逐層抽絲剝繭,由淺及深將分布式事務的實現原理呈現給大家。
一、事務簡介
1、事務的本質
事務的核心是鎖和並發,采用同步控制的方式保證並發的情況下性能盡可能高,且容易理解。
計算機可以簡單的理解為一個標準的打字機,盡管看起來計算機可以並行處理很多事情,但實際上每個CPU單位時間內只能做一件事,要麽讀取數據、要麽計算數據、要麽寫入數據,所有的任務都可以看成這三件事的集合。
計算機的這種特性引出了一個問題:當多個人去讀、算、寫操作時,如果不加訪問控制,系統勢必會產生沖突。而事務相當於在讀、算、寫操作之外增加了同步的模塊,進而保證只有一個線程進入事務當中,而其他線程不會進入。這樣的方法其實就是我們提的事務。
2、事務單元
事務單元是通過Begin-Traction,然後Commit(Begin-Traction、Commit和Rollback之間所有針對數據的寫入、讀取的操作都應該添加同步訪問),Begin和Commit之間就是一個同步的事務單元。例如,Bob給Smith 100塊錢就是一個事務單元,這個過程中有很多步操作,具體如下圖所示;但對業務來說,僅是一個轉賬的操作。
當三個賬戶都在進行轉賬操作時,每個操作都涉及Smith賬戶,所有的事務都會排隊,形成一組事務單元。
事務單元之間的Happen-Before關系中的四種可能性:讀寫、寫讀、讀讀、寫寫。所有事務之間的關系都可以抽象成這四種之一,來對應現在所有的業務邏輯處理。在此基礎之上,需要用最快的速度處理多個事務單元之間的關系,同時還能保障這四種操作的邏輯順序。
3、兩階段鎖協議
Two Phase Lock(2PL)是數據庫中非常重要的一個概念。數據庫操作Insert、Update、Delete都是先讀再寫的操作。數據庫利用這些操作的特性,在每一次查詢過程中,只要查到數據,就會在該數據上加鎖。理論上,所有被讀取的數據都已加鎖,不會再被其他人讀到,也就是說對數據進行的中間操作狀態對所有人都不可見,當所有中間狀態完成後,提交操作時,解開鎖,此時數據對所有系統可見,
例如在轉賬過程中,所有人只能看到兩種狀態:開始時,A有錢,B沒錢;結束時,B有錢,A沒錢,而中間A減掉錢,B尚未加上錢的狀態被鎖隱藏掉了,這個操作就是數據庫中處理事務的最標準的方式。如上圖所示:事務中的Trx2(JoeLock)與其他事務不相關,因此可以並行執行;Trx1需要Lock兩個數據Boblock和Smithlock,而Trx3同樣需要Lock這兩個數據,因此Trx3必須等待,且等待在Boblock上;Joe事務會先結束,Trx3會等到Trx1完成後才會開始。
二、單機事務常見處理方法
單機事務的常見處理方法有排隊法、排他鎖、讀寫鎖、MVCC等方式,下面來一一解析。
1、排隊法
事務處理中最重要也是最簡單的方案是排隊法,單線程地處理一堆數據。
在排隊法中,在所有事務單元的所有的讀事務,讀寫事務都是串行的方式來進行,這種方案的實現簡單,但是並發性最差。
2、排它鎖
排它鎖是針對同一個事務單元的數據進行訪問控制。在上述講解的排隊法中,所有事務單元均以單線程的方式進行操作,但是有些場景不適合用單線程操作,可以利用排他鎖的方式來快速隔離並發讀寫事務。數據庫中有一些事務單元是共享的,如圖中的事務單元1是共享的,事務單元2/3共享數據;針對事務單元2/3共享數據的所有讀寫Block住,事務單元1單獨用一個鎖來控制,用這種方式完成系統的訪問控制。
3、讀寫鎖
如果是一個只讀的事務,例如只對數據進行查詢操作,在該過程中數據一定不被修改,因此多個查詢操作可以並行執行,因此一種針對讀讀場景的優化自然而然產生——讀寫鎖。讀寫鎖的核心是在多次讀的操作中,同時允許多個讀者來訪問共享資源,提高並發性。
4、MVCC(多版本並發控制)
MVCC的本質是Copy On Write,也就是每次寫都是以重新開始一個新的版本的方式寫入數據,因此,數據庫中也就包含了之前的所有版本。在數據讀的過程中,先申請一個版本號,如果該版本號小於正在寫入的版本號,則數據一定可以查詢到,無需等到新版本完全寫完即可返回查詢結果。這種方式可以在讀讀不阻塞的前提下,實現讀寫/寫讀不阻塞,盡可能保證所有的讀操作並行,而寫操作串行。
MVCC的兩種不同實現方式:
第一種實現方式是將數據記錄的多個版本保存在數據庫中,當這些不同版本數據不再需要時,垃圾收集器回收這些記錄。這個方式被PostgreSQL和Firebird/Interbase采用,SQL Server使用的類似機制,所不同的是舊版本數據不是保存在數據庫中,而保存在不同於主數據庫的另外一個數據庫tempdb中/
第二種實現方式只在數據庫保存最新版本的數據,但是會在使用undo時動態重構舊版本數據,這種方式被Oracle和MySQL/InnoDB使用
三、分布式事務的處理方案
分布式事務是指會涉及到操作多個數據庫的事務。其實就是將對同一庫事務的概念擴大到了對多個庫的事務。目的是為了保證分布式系統中的數據一致性。分布式事務處理的關鍵是必須有一種方法可以知道事務在任何地方所做的所有動作,提交或回滾事務的決定必須產生統一的結果(全部提交或全部回滾),要想理解分布式事務,我們需要先介紹一下兩階段提交協議。
1、2PC(兩階段提交)
兩階段提交協議(Two-phase Commit,2PC)經常被用來實現分布式事務。一般分為協調器和若幹事務執行者兩種角色。這裏的事務執行者就是具體的數據庫,抽象點可以說是可以控制給數據庫的程序。 協調器可以和事務執行器在一臺機器上。
在分布式系統中,每個節點雖然可以知曉自己的操作的成功或者失敗,卻無法知道其他節點的操作的成功或失敗。當一個事務跨越多個節點時,為了保持事務的ACID特性,需要引入一個作為協調者的組件來統一掌控所有節點(稱作參與者)。
所謂的兩個階段是指:第一階段:準備階段(投票階段)和第二階段:提交階段(執行階段)。
1.準備階段:事務協調者(事務管理器)給每個參與者(資源管理器)發送Prepare消息,每個參與者要麽直接返回失敗,要麽在本地執行事務,寫本地的redo和undo日誌,但不提交
2.提交階段:如果協調者收到了參與者的失敗消息或者超時,直接給每個參與者發送回滾(Rollback)消息;否則,發送提交(Commit)消息;參與者根據協調者的指令執行提交或者回滾操作,釋放所有事務處理過程中使用的鎖資源。(註意:必須在最後階段釋放鎖資源)。
二階段提交看起來確實能夠提供原子性的操作,但是不幸的是,二階段提交還是有幾個缺點的:
1、同步阻塞問題。執行過程中,所有參與節點都是事務阻塞型的。當參與者占有公共資源時,其他第三方節點訪問公共資源不得不處於阻塞狀態
2、單點故障。由於協調者的重要性,一旦協調者發生故障。參與者會一直阻塞下去。尤其在第二階段,協調者發生故障,那麽所有的參與者還都處於鎖定事務資源的狀態中,而無法繼續完成事務操作。(如果是協調者掛掉,可以重新選舉一個協調者,但是無法解決因為協調者宕機導致的參與者處於阻塞狀態的問題)
3、數據不一致。在二階段提交的階段二中,當協調者向參與者發送commit請求之後,發生了局部網絡異常或者在發送commit請求過程中協調者發生了故障,這回導致只有一部分參與者接受到了commit請求。而在這部分參與者接到commit請求之後就會執行commit操作。但是其他部分未接到commit請求的機器則無法執行事務提交。於是整個分布式系統便出現了數據部一致性的現象。
4、二階段無法解決的問題:協調者再發出commit消息之後宕機,而唯一接收到這條消息的參與者同時也宕機了。那麽即使協調者通過選舉協議產生了新的協調者,這條事務的狀態也是不確定的,沒人知道事務是否被已經提交。
由於二階段提交存在著諸如同步阻塞、單點問題等缺陷,所以,研究者們在二階段提交的基礎上做了改進,提出了三階段提交。
2、3PC(三階段提交)
3PC把2PC的準備階段再次一分為二,這樣三階段提交就有CanCommit、PreCommit、DoCommit三個階段。
CanCommit階段:3PC的CanCommit階段其實和2PC的準備階段很像。協調者向參與者發送commit請求,參與者如果可以提交就返回Yes響應,否則返回No響應。
PreCommit階段:協調者根據參與者的反應情況來決定是否可以記性事務的PreCommit操作。根據響應情況,有以下兩種可能:
- 假如協調者從所有的參與者獲得的反饋都是Yes響應,那麽就會執行事務的預執行。
- 假如有任何一個參與者向協調者發送了No響應,或者等待超時之後,協調者都沒有接到參與者的響應,那麽就執行事務的中斷。
doCommit階段:該階段進行真正的事務提交
相對於2PC,3PC主要解決的單點故障問題,並減少阻塞,因為一旦參與者無法及時收到來自協調者的信息之後,他會默認執行commit。而不會一直持有事務資源並處於阻塞狀態。但是這種機制也會導致數據一致性問題,因為,由於網絡原因,協調者發送的abort響應沒有及時被參與者接收到,那麽參與者在等待超時之後執行了commit操作。這樣就和其他接到abort命令並執行回滾的參與者之間存在數據不一致的情況。
分布式事務原理與實踐