分散式事務:相關概念
相關概念整理
1 事務
1.1 ACID
ACID,指資料庫事務正確執行的四個基本要素的縮寫。包含:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、永續性(Durability)。一個支援事務(Transaction)的資料庫,必須要具有這四種特性,否則在事務過程(Transaction processing)當中無法保證資料的正確性,交易過程極可能達不到交易方的要求。
原子性(Atomicity)
整個事務中的所有操作,要麼全部完成,要麼全部不完成,不可能停滯在中間某個環節。事務在執行過程中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過一樣。
就像你買東西要麼交錢收貨一起都執行,要麼要是發不出貨,就退錢。
一致性(Consistency)
一個事務可以封裝狀態改變(除非它是一個只讀的)。事務必須始終保持系統處於一致的狀態,不管在任何給定的時間併發事務有多少。
也就是說:如果事務是併發多個,系統也必須如同序列事務一樣操作。其主要特徵是保護性和不變性(Preserving an Invariant),以轉賬案例為例,假設有五個賬戶,每個賬戶餘額是100元,那麼五個賬戶總額是500元,如果在這個5個賬戶之間同時發生多個轉賬,無論併發多少個,比如在A與B賬戶之間轉賬5元,在C與D賬戶之間轉賬10元,在B與E之間轉賬15元,五個賬戶總額也應該還是500元,這就是保護性和不變性。
隔離性(Isolation)
隔離狀態執行事務,使它們好像是系統在給定時間內執行的唯一操作。如果有兩個事務,執行在相同的時間內,執行相同的功能,事務的隔離性將確保每一事務在系統中認為只有該事務在使用系統。這種屬性有時稱為序列化,為了防止事務操作間的混淆,必須序列化或序列化請求,使得在同一時間僅有一個請求用於同一資料。
打個比方,你買東西這個事情,是不影響其他人的。
永續性(Durability)
在事務完成以後,該事務對資料庫所作的更改便持久的儲存在資料庫之中,並不會被回滾。
只要事務成功結束,它對資料庫所做的更新就必須永久儲存下來。即使發生系統崩潰,重新啟動資料庫系統後,資料庫還能恢復到事務成功結束時的狀態。
打個比方,你買東西的時候需要記錄在賬本上,即使老闆忘記了那也有據可查。
1.2 ACID的侷限
資料庫事務必須保證ACID,在ofollow,noindex">2PC 文章裡,探討了跨資料庫事務是如何保證ACID的。
當資料量越來越大的時候,我們會對將大資料庫拆分成若干小庫,隨著資料庫數量越來越多,2PC(及XA)就顯得有些捉襟見肘了:
- 效能低下,2PC協議是阻塞式的。當協調的資料庫越來越多時,效能無法接受。
- 無法水平擴充套件以提升效能,只能靠垂直擴充套件(提升硬體)——更快的CPU、更快更大的硬碟、更大更快的記憶體——但是這樣很貴,並且很容易遇到極限。
1.3 事務隔離
在提到隔離性的時候我們提到,在修改同一份資料的情況下,兩個事務必須挨個執行以免出現衝突情況。而資料庫有四種隔離級別(注意:不是所有資料庫支援所有隔離級別)
Isolation Level | Dirty Reads | Non-Repeatable Reads | Phantom Reads |
---|---|---|---|
Read uncommitted | 允許 | 允許 | 允許 |
Read committed | 不允許 | 允許 | 允許 |
Repeatable reads | 不允許 | 不允許 | 允許 |
Serializable | 不允許 | 不允許 | 不允許 |
PS. 大多數資料庫的預設隔離級別是Read committed。
來複習一下Dirty reads、Non-repeatable reads、Phantom reads的概念:
- Dirty reads:A事務可以讀到B事務還未提交的資料
- Non-repeatable read:A事務讀取一行資料,B事務後續修改了這行資料,A事務再次讀取這行資料,結果得到的資料不同。
-
Phantom reads:A事務通過
SELECT ... WHERE
得到一些行,B事務插入新行或者更新已有的行使得這些行滿足A事務的WHERE
條件,A事務再次SELECT ... WHERE
結果比上一次多了一些行。
大多數資料庫在實現以上事務隔離級別(Read uncommitted除外)時採用的機制是鎖。這也就是為什麼經常說當應用程式裡大量使用事務或者高併發情況下會出現效能低下、死鎖的問題。
2 分散式事務
2.1 CAP
CAP理論是Eric Brewer教授針對分散式資料庫所提出的一套理論,他認為在實現分散式資料庫,需要考慮3個需求:
- Consistency(一致性):當寫發生時,每個節點的資料必須都被更新到。當查詢發生時,如果還未全部更新到則返回error或timeout;如果全部更新到了,則直接返回結果。
-
Availability(可用性):當查詢發生時,不用考慮上一次寫是否更新到了每個節點,直接提供當下的資料作為結果。
- 有限時間內:對於使用者的一個操作請求,系統必須能夠在指定的時間(響應時間)內返回對應的處理結果,如果超過了這個時間範圍,那麼系統就被認為是不可用的。即這個響應時間必須在一個合理的值內,不讓使用者感到失望。
- 返回正常結果:要求系統在完成對使用者請求的處理後,返回一個正常的響應結果。正常的響應結果通常能夠明確地反映出對請求的處理結果,即成功或失敗,而不是一個讓使用者感到困惑的返回結果。比如返回一個系統錯誤如OutOfMemory,則認為系統是不可用的。
- Partition-tolerance(分割槽容錯性):除非所有節點都掛,它都應該能繼續提供服務。
CAP理論指出,當每個節點都工作正常的時候,C、A、P是可以都滿足的,當出現節點故障時,我們只能3選2。
如果我們不想因為資料庫的某個節點出現故障就讓資料庫停止服務,那麼我們必定選擇P,那麼我們就只能在C和A之間做選擇。如果選擇C:後續的讀都將失敗。如果選擇A:會讀到的結果。
CAP | 說明 |
---|---|
放棄P | 如果希望能夠避免系統出現分割槽容錯性問題,一種較為簡單的做法是將所有的資料(或者僅僅是哪些與事務相關的資料)都放在一個分散式節點上。這樣做雖然無法100%保證系統不會出錯,但至少不會碰到由於網路分割槽帶來的負面影響。但同時需要注意的是,放棄P的同時也就意味著放棄了系統的可擴充套件性 |
放棄A | 一旦系統遇到網路分割槽或其他故障或為了保證一致性時,放棄可用性,那麼受到影響的服務需要等待一定的時間,因此在等待期間系統無法對外提供正常的服務,即不可用 |
放棄C | 這裡所說的放棄一致性,實際上指的是放棄資料的強一致性,而保留資料的最終一致性。這樣的系統無法保證資料保持實時的一致性,但是能夠承諾的是,資料最終會達到一個一致的狀態。 |
需要明確的一點是:對於一個分散式系統而言,分割槽容錯性可以說是一個最基本的要求。因為既然是一個分散式系統,那麼分散式系統中的元件必然需要被部署到不同的節點,否則也就無所謂的分散式系統了,因此必然出現子網路。而對於分散式系統而言,網路問題又是一個必定會出現的異常情況,因此分割槽容錯性也就成為了一個分散式系統必然需要面對和解決的問題。因此係統架構師往往需要把精力花在如何根據業務特點在C(一致性)和A(可用性)之間尋求平衡。
2.2 BASE
BasicallyA vailable,S oft state,E ventually consistent,簡稱BASE。
Basically Available(基本可用)
基本可用是指分散式系統在出現不可預知的故障的時候,允許損失部分可用性,但不等於系統不可用。
- 響應時間上的損失:當出現故障時,響應時間增加
- 功能上的損失:當流量高峰期時,遮蔽一些功能的使用以保證系統穩定性(服務降級)
Soft state(軟狀態)
與硬狀態相對,即是指允許系統中的資料存在中間狀態,並認為該中間狀態的存在不會影響系統的整體可用性,即允許系統在不同節點的資料副本之間進行資料同步的過程存在延時。
Eventually consistent(最終一致性)
強調系統中所有的資料副本,在經過一段時間的同步後,最終能夠達到一個一致的狀態。其本質是需要系統保證最終資料能夠達到一致,而不需要實時保證系統資料的強一致性。
最終一致性可分為如下幾種:
- 因果一致性(Causal consistency):即程序A在更新完資料後通知程序B,那麼之後程序B對該項資料的範圍都是程序A更新後的最新值。
- 讀己之所寫(Read your writes):程序A更新一項資料後,它自己總是能訪問到自己更新過的最新值。
- 會話一致性(Session consistency):將資料一致性框定在會話當中,在一個會話當中實現讀己之所寫的一致性。即執行更新後,客戶端在同一個會話中始終能讀到該項資料的最新值
- 單調讀一致性(Monotonic read consistency):如果一個程序從系統中讀取出一個數據項的某個值後,那麼系統對於該程序後續的任何資料訪問都不應該返回更舊的值。
- 單調寫一致性(Monotoic write consistency):一個系統需要保證來自同一個程序的寫操作被順序執行。
BASE定理是提出通過犧牲一致性來獲得可用性,並允許資料在一段時間內是不一致的,但最終達到一致狀態 。
BASE和ACID相反,ACID是悲觀的,它要求所有操作都必須保證一致性,而BASE是樂觀的,它接受資料庫的一致性在不斷變化當中。同時,BASE對於CAP中的C做出了一定的妥協——接受臨時的不一致,採用最終一致性。最終一致性,聽上去怪怪的,一些開發人員覺得這是個壞東西。不過我們真的要時時刻刻 保證一致性嗎?BASE認為我們可以做一些妥協,因此如果我們按照BASE設計系統的話就能夠保證:
-
ACID
- ACID - A,不保證,一旦開始“寫”則不可能回滾。
- ACID - C,保證最終一致性。
- ACID - I,不保證,是因為一個大事務是由多個小事務組成,每個小事務都會獨立提交。
- ACID - D,保證,因為資料庫保證D。
-
CAP
- CAP - C,保證最終一致性。
- CAP - A,保證基本可用。
- CAP - P,保證。
舉個例子,現在我們有3條insert要執行(至於是否是3個不同的表、資料庫不重要),那麼只要保證下面幾點就能夠滿足BASE:
- 最終都能夠執行成功
- 任何一條語句執行失敗都會重試
- 任意一條語句重複執行結果都一樣——冪等
正確地使用BASE模式也不是那麼容易,比如刷卡消費業務,我們要保證“檢查餘額”和“扣款、記錄消費日誌”這兩組動作不會產生交叉,否則就會因為高併發場景而發生透支,在這個例子裡我們可以對“扣款、記錄消費日誌”做最終一致性,但是如何保證下達“扣款、記錄消費日誌”這兩個指令肯定不會產生透支的情況則不是BASE解決的問題了。
所以總結一下BASE的特點就是:
- 解決的是提交的問題
- 2PC將提交動作放在資料庫,而BASE將提交動作放在應用程式