分佈式事務原理與實踐

 

  所謂事務,它是一個操做集合,這些操做要麼都執行,要麼都不執行,它是一個不可分割的工做單位。好比網上訂票,要麼你定票成功,餘票減一張; 要麼你定票失敗,餘票的數量不變。這就要求購票和餘票減小這兩個不一樣的操做必須放在一塊兒,成爲一個完整的邏輯鏈,這樣就構成了一個事務。 數據庫

  事務提及來很簡單,就是BEGIN TRAN,COMMIT TRAN,ROLLBACK TRAN 三個語句,可是事務的內部實現原理是很是複雜的,而分佈式事務因爲須要跨越多個服務器,操做多個數據庫,複雜度比單機事務要複雜的多,本文嘗試經過鎖和併發控制的講解,逐層抽絲剝繭,由淺及深將分佈式事務的實現原理呈現給你們。 服務器

     

1、事務簡介 網絡

     

一、事務的本質併發

  事務的核心是鎖和併發,採用同步控制的方式保證併發的狀況下性能儘量高,且容易理解。 app

            

     

 

     

     

     

     

     

   

   

   

 

 

 

 

  計算機能夠簡單的理解爲一個標準的打字機,儘管看起來計算機能夠並行處理不少事情,但實際上每一個CPU單位時間內只能作一件事,要麼讀取數據、要麼計算數據、要麼寫入數據,全部的任務均可以當作這三件事的集合。 分佈式

  計算機的這種特性引出了一個問題:當多我的去讀、算、寫操做時,若是不加訪問控制,系統勢必會產生衝突。而事務至關於在讀、算、寫操做以外增長了同步的模塊,進而保證只有一個線程進入事務當中,而其餘線程不會進入。這樣的方法其實就是咱們提的事務。高併發

     

二、事務單元 性能

  事務單元是經過Begin-Traction,而後Commit(Begin-Traction、Commit和Rollback之間全部針對數據的寫入、讀取的操做都應該添加同步訪問),Begin和Commit之間就是一個同步的事務單元。例如,Bob給Smith 100塊錢就是一個事務單元,這個過程當中有不少步操做,具體以下圖所示;但對業務來講,僅是一個轉帳的操做。 優化

     

  當三個帳戶都在進行轉帳操做時,每一個操做都涉及Smith帳戶,全部的事務都會排隊,造成一組事務單元。
  事務單元之間的Happen-Before關係中的四種可能性:讀寫、寫讀、讀讀、寫寫。全部事務之間的關係均可以抽象成這四種之一,來對應如今全部的業務邏輯處理。在此基礎之上,須要用最快的速度處理多個事務單元之間的關係,同時還能保障這四種操做的邏輯順序。
spa

     

     

     

     

     

三、兩階段鎖協議

  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完成後纔會開始。

     

 2、單機事務常見處理方法

單機事務的常見處理方法有排隊法、排他鎖、讀寫鎖、MVCC等方式,下面來一一解析。

一、排隊法

  事務處理中最重要也是最簡單的方案是排隊法,單線程地處理一堆數據。

在排隊法中,在全部事務單元的全部的讀事務,讀寫事務都是串行的方式來進行,這種方案的實現簡單,可是併發性最差。

     

二、排它鎖

  排它鎖是針對同一個事務單元的數據進行訪問控制。在上述講解的排隊法中,全部事務單元均以單線程的方式進行操做,可是有些場景不適合用單線程操做,能夠利用排他鎖的方式來快速隔離併發讀寫事務。數據庫中有一些事務單元是共享的,如圖中的事務單元1是共享的,事務單元2/3共享數據;針對事務單元2/3共享數據的全部讀寫Block住,事務單元1單獨用一個鎖來控制,用這種方式完成系統的訪問控制。

     

     

     

三、讀寫鎖

若是是一個只讀的事務,例如只對數據進行查詢操做,在該過程當中數據必定不被修改,所以多個查詢操做能夠並行執行,所以一種針對讀讀場景的優化天然而然產生——讀寫鎖。讀寫鎖的核心是在屢次讀的操做中,同時容許多個讀者來訪問共享資源,提升併發性。

 

     

     

     

     

     

     

   

四、MVCC(多版本併發控制)

  MVCC的本質是Copy On Write,也就是每次寫都是以從新開始一個新的版本的方式寫入數據,所以,數據庫中也就包含了以前的全部版本。在數據讀的過程當中,先申請一個版本號,若是該版本號小於正在寫入的版本號,則數據必定能夠查詢到,無需等到新版本徹底寫完便可返回查詢結果。這種方式能夠在讀讀不阻塞的前提下,實現讀寫/寫讀不阻塞,儘量保證全部的讀操做並行,而寫操做串行。

       

     

 

     

   

   

   

   

   

 

 

  MVCC的兩種不一樣實現方式:

  第一種實現方式是將數據記錄的多個版本保存在數據庫中,當這些不一樣版本數據再也不須要時,垃圾收集器回收這些記錄。這個方式被PostgreSQL和Firebird/Interbase採用,SQL Server使用的相似機制,所不一樣的是舊版本數據不是保存在數據庫中,而保存在不一樣於主數據庫的另一個數據庫tempdb中/

  第二種實現方式只在數據庫保存最新版本的數據,可是會在使用undo時動態重構舊版本數據,這種方式被Oracle和MySQL/InnoDB使用

     

3、分佈式事務的處理方案

  分佈式事務是指會涉及到操做多個數據庫的事務。其實就是將對同一庫事務的概念擴大到了對多個庫的事務。目的是爲了保證分佈式系統中的數據一致性。分佈式事務處理的關鍵是必須有一種方法能夠知道事務在任何地方所作的全部動做,提交或回滾事務的決定必須產生統一的結果(所有提交或所有回滾),要想理解分佈式事務,咱們須要先介紹一下兩階段提交協議。

     

一、2PC(兩階段提交)

  兩階段提交協議(Two-phase Commit,2PC)常常被用來實現分佈式事務。通常分爲協調器和若干事務執行者兩種角色。這裏的事務執行者就是具體的數據庫,抽象點能夠說是能夠控制給數據庫的程序。 協調器能夠和事務執行器在一臺機器上。

  在分佈式系統中,每一個節點雖然能夠知曉本身的操做的成功或者失敗,卻沒法知道其餘節點的操做的成功或失敗。當一個事務跨越多個節點時,爲了保持事務的ACID特性,須要引入一個做爲協調者的組件來統一掌控全部節點(稱做參與者)。

     

所謂的兩個階段是指:第一階段:準備階段(投票階段)和第二階段:提交階段(執行階段)

1.準備階段:事務協調者(事務管理器)給每一個參與者(資源管理器)發送Prepare消息,每一個參與者要麼直接返回失敗,要麼在本地執行事務,寫本地的redo和undo日誌,但不提交

2.提交階段:若是協調者收到了參與者的失敗消息或者超時,直接給每一個參與者發送回滾(Rollback)消息;不然,發送提交(Commit)消息;參與者根據協調者的指令執行提交或者回滾操做,釋放全部事務處理過程當中使用的鎖資源。(注意:必須在最後階段釋放鎖資源)。

     

二階段提交看起來確實可以提供原子性的操做,可是不幸的是,二階段提交仍是有幾個缺點的:

一、同步阻塞問題。執行過程當中,全部參與節點都是事務阻塞型的。當參與者佔有公共資源時,其餘第三方節點訪問公共資源不得不處於阻塞狀態

二、單點故障。因爲協調者的重要性,一旦協調者發生故障。參與者會一直阻塞下去。尤爲在第二階段,協調者發生故障,那麼全部的參與者還都處於鎖定事務資源的狀態中,而沒法繼續完成事務操做。(若是是協調者掛掉,能夠從新選舉一個協調者,可是沒法解決由於協調者宕機致使的參與者處於阻塞狀態的問題)

三、數據不一致。在二階段提交的階段二中,當協調者向參與者發送commit請求以後,發生了局部網絡異常或者在發送commit請求過程當中協調者發生了故障,這回致使只有一部分參與者接受到了commit請求。而在這部分參與者接到commit請求以後就會執行commit操做。可是其餘部分未接到commit請求的機器則沒法執行事務提交。因而整個分佈式系統便出現了數據部一致性的現象。

四、二階段沒法解決的問題:協調者再發出commit消息以後宕機,而惟一接收到這條消息的參與者同時也宕機了。那麼即便協調者經過選舉協議產生了新的協調者,這條事務的狀態也是不肯定的,沒人知道事務是否被已經提交。

因爲二階段提交存在着諸如同步阻塞、單點問題等缺陷,因此,研究者們在二階段提交的基礎上作了改進,提出了三階段提交。

     

二、3PC(三階段提交)

  3PC把2PC的準備階段再次一分爲二,這樣三階段提交就有CanCommit、PreCommit、DoCommit三個階段。

CanCommit階段:3PC的CanCommit階段其實和2PC的準備階段很像。協調者向參與者發送commit請求,參與者若是能夠提交就返回Yes響應,不然返回No響應。

PreCommit階段:協調者根據參與者的反應狀況來決定是否能夠記性事務的PreCommit操做。根據響應狀況,有如下兩種可能:

  • 假如協調者從全部的參與者得到的反饋都是Yes響應,那麼就會執行事務的預執行。
  • 假若有任何一個參與者向協調者發送了No響應,或者等待超時以後,協調者都沒有接到參與者的響應,那麼就執行事務的中斷。

doCommit階段:該階段進行真正的事務提交

     

     

 

     

     

     

     

 

 

  相對於2PC,3PC主要解決的單點故障問題,並減小阻塞,由於一旦參與者沒法及時收到來自協調者的信息以後,他會默認執行commit。而不會一直持有事務資源並處於阻塞狀態。可是這種機制也會致使數據一致性問題,由於,因爲網絡緣由,協調者發送的abort響應沒有及時被參與者接收到,那麼參與者在等待超時以後執行了commit操做。這樣就和其餘接到abort命令並執行回滾的參與者之間存在數據不一致的狀況。

相關文章
相關標籤/搜索