天天看點

ACID一緻性與CAP一緻性淺談一、概述二、ACID一緻性的分級三、CAP一緻性的分級

一、概述

一緻性在越來越多的場景中被提到,但是在資料庫與分布式這兩個領域中,一緻性卻是兩個完全不同的了解,許多對這兩個領域了解不是特别清楚的,便非常容易混淆。

資料庫領域:

  • 即ACID中的C,主要是指事務開始前和結束後,資料庫的完整性限制沒有被破壞。比如A向B轉賬,不可能A扣了錢,B卻沒收到。

分布式領域:

  • 即CAP中的C,主要指多副本之間,資料的同步一緻,比如存在A、B、C三個副本,A中寫入資料 hello,寫完馬上讀B和C,就一定要讀出 hello,讀出來我們就稱之為符合一緻性。

二、ACID一緻性的分級

在資料庫事務中,可能存在丢失修改、不能重複讀、髒讀等問題,它們都是由于并發事務在修改同一份資料的時候導緻的問題,此類問題可以通過對同一個資源加鎖的方式來解決,而最後一種情況是由于不同僚務并發時,新增資料導緻的問題,對于新增的記錄是無法加鎖的,此種情況隻能通過事務的串行化來解決。而串行化與并發是沖突的,是以要在性能和事務的一緻性強度上取得一個平衡,就涉及到不同的隔離等級。

2.1 不一緻情況

在多個事務并行運作時,可能會出現以下問題,影響資料庫的一緻性:

  • 修改丢失:丢失修改是事務A和B先後更改資料資料x(假設初始是x0),但是在A未正式更改前,B已經讀取了原先的資料x0,最後A更改後為x1,B更改的并不是A更新後的x1,而是更改的x0,更改後假設為x2,這時x2将x1覆寫了,相當于事務A針對x的更改丢失了。
  • 髒讀: 事務T1讀取了T2更改的x,但是T2在實際存儲資料時可能出錯復原了,這時T1讀取的實際是無效的資料,這種情況下就是髒讀
  • 不可重複讀:是說在T1讀取x時,由于中間T2更改了x,是以T1前後兩次讀取的x值不相同,這就是所謂的不可重複讀
  • 幻讀:在T1讀取符合某個條件的所有記錄時,T2增加了一條符合該條件的記錄,這就導緻T1執行過程中前後讀取的記錄可能不一緻,即T2之後讀取時會多出一條記錄。

2.2 隔離級别

事務的隔離級别從低到高有:讀未送出(Read uncommitted)、讀送出(read committed)、可重複讀(repeatable read)和串行化(Serializable)

  1. Read Uncommitted:事務讀資料時不會加鎖,寫資料時會有行級共享鎖。假設事務1先于事務2,當事務1更新資料的時候,事務2可以讀取事務1未送出的資料,但是不能更新事務1正在更新的資料。而如果事務1隻是讀資料,那麼事務2既可以讀資料,也可以更新資料。這種情況下無法規避髒讀,不可重複讀的問題。
  2. Read Committed:即在一個事務修改資料過程中,如果事務還沒送出,其他事務不能讀該資料,或者說隻能讀取committed的資料。事務讀資料的瞬間會加行級共享鎖,一旦讀完該行,立即釋放該行級共享鎖;而寫資料的瞬間會加行級排它鎖,直到事務結束。這種情況下就避免了髒讀,但是卻不能避免不可重複讀的問題
  3. Repeatable Read:當然就再升一級,為的就是避免不可重複讀的問題,是以名字叫repeatable read。怎麼實作的呢,我們知道read committed是,事務讀操作隻在讀的一瞬間加鎖,讀完這行就釋放鎖了,而repeatable read級别是讀的一瞬間加鎖,但是一直到事務結束才釋放鎖。但是repeatable read不能解決幻讀的問題,因為幻讀是增加記錄,并不是更改原先的記錄。
  4. Serialization:到達這一級别的隔離,可以徹底解決一緻性的所有問題。一般來說是通過加表鎖來解決串行化的問題。

三、CAP一緻性的分級

在分布式系統中,一緻性指在某寫操作後,任何讀操作,都應該擷取到該寫操作寫入的那個最新的值。相當于要求分布式系統中的各節點時時刻刻保持資料的一緻性。依據資料在節點中同步,讀取可能出現的錯誤情況,可以将一緻性分為以下幾個等級:

  • 強一緻性:指系統中的某個資料被成功更新後,後續在任何節點、對該資料進行任何讀取操作,都将得到更新後的值
  • 弱一緻性:弱一緻性是相對于強一緻性而言,它不保證總能得到最新的值;
  • 最終一緻性:是弱一緻性的特殊形式,即保證在沒有新的更新的條件下,經過一段“不一緻時間視窗”,最終所有的通路都是最後更新的值。最常見的是DNS服務,更新域名指向的機器後,多級緩存要等到expiration time的時候才會更新,但是随着時間的推移,最終資料會趨于一緻。

最終一緻性根據更新資料後各程序通路到資料的時間和方式的不同,又可以區分為:

  • 因果一緻性。如果程序A通知程序B它已更新了一個資料項,那麼程序B的後續通路将傳回更新後的值,且一次寫入将保證取代前一次寫入。與程序A無因果關系的程序C的通路遵守一般的最終一緻性規則。
  • “讀己之所寫(read-your-writes)”一緻性。當程序A自己更新一個資料項之後,它總是通路到更新過的值,絕不會看到舊值。這是因果一緻性模型的一個特例。
  • 會話(Session)一緻性。這是上一個模型的實用版本,它把通路存儲系統的程序放到會話的上下文中。隻要會話還存在,系統就保證“讀己之所寫”一緻性。如果由于某些失敗情形令會話終止,就要建立新的會話,而且系統的保證不會延續到新的會話。
  • 單調(Monotonic)讀一緻性。如果程序已經看到過資料對象的某個值,那麼任何後續通路都不會傳回在那個值之前的值。
  • 單調寫一緻性。系統保證來自同一個程序的寫操作順序執行。要是系統不能保證這種程度的一緻性,就非常難以程式設計了。

上述最終一緻性的不同方式可以進行組合,例如單調讀一緻性和讀己之所寫一緻性就可以組合實作。

一緻性與副本數:

從服務端角度,如何盡快将更新後的資料分布到整個系統,降低達到最終一緻性的時間視窗,是提高系統的可用度和使用者體驗非常重要的方面。對于分布式資料系統:

  • N — 資料複制的份數
  • W — 更新資料是需要保證寫完成的節點數
  • R — 讀取資料的時候需要讀取的節點數

如果W+R>N,寫的節點和讀的節點重疊,則是強一緻性。例如對于典型的一主一備同步複制的關系型資料庫,N=2,W=2,R=1,則不管讀的是主庫還是備庫的資料,都是一緻的。

如果W+R<=N,則是弱一緻性。例如對于一主一備異步複制的關系型資料庫,N=2,W=1,R=1,則如果讀的是備庫,就可能無法讀取主庫已經更新過的資料,是以是弱一緻性。

對于分布式系統,為了保證高可用性,一般設定N>=3。不同的N,W,R組合,是在可用性和一緻性之間取一個平衡,以适應不同的應用場景。

  • 如果N=W,R=1,任何一個寫節點失效,都會導緻寫失敗,是以可用性會降低,但是由于資料分布的N個節點是同步寫入的,是以可以保證強一緻性。
  • 如果N=R,W=1,隻需要一個節點寫入成功即可,寫性能和可用性都比較高。但是讀取其他節點的程序可能不能擷取更新後的資料,是以是弱一緻性。這種情況下,如果W<(N+1)/2,并且寫入的節點不重疊的話,則會存在寫沖突  
ACID一緻性與CAP一緻性淺談一、概述二、ACID一緻性的分級三、CAP一緻性的分級