事務ACID特性與隔離級別

Caizhenhao發表於2019-05-11
事務指的是滿足 ACID 特性的一組操作,可以通過 Commit 提交一個事務,也可以使用 Rollback 進行回滾。

ACID

從業務角度出發,對資料庫的一組操作要求保持4個特徵:

Atomicity(原子性):

一個事務必須被視為一個不可分割的最小工作單元,整個事務中的所有操作要麼全部提交成功,要麼全部失敗回滾,對於一個事務來說,不可能只執行其中的一部分操作。

Consistency(一致性):

資料庫在事務執行前後都保持一致性狀態。在一致性狀態下,所有事務對一個資料的讀取結果都是相同的。
資料庫總是從一個一致性狀態轉換到另一個一致狀態。

Isolation(隔離性):

通常來說,一個事務所做的修改在最終提交以前,對其他事務是不可見的。注意這裡的“通常來說”,後面的事務隔離級級別會說到。

Durability(永續性):

一旦事務提交,則其所做的修改就會永久儲存到資料庫中。即使系統發生崩潰,事務執行的結果也不能丟失。使用重做日誌來保證永續性。(永續性的安全性與重新整理日誌級別也存在一定關係,不同的級別對應不同的資料安全級別。)

事務的 ACID 特性概念簡單,但不是很好理解,主要是因為這幾個特性不是一種平級關係:
  • 只有滿足一致性,事務的執行結果才是正確的。
  • 在無併發的情況下,事務序列執行,隔離性一定能夠滿足。此時只要能滿足原子性,就一定能滿足一致性。
  • 在併發的情況下,多個事務並行執行,事務不僅要滿足原子性,還需要滿足隔離性,才能滿足一致性。
  • 事務滿足持久化是為了能應對資料庫崩潰的情況。
事務ACID特性與隔離級別

AUTOCOMMIT

MySQL 預設採用自動提交模式。也就是說,如果不顯式使用START TRANSACTION語句來開始一個事務,那麼每個查詢都會被當做一個事務自動提交。

併發一致性問題

在併發環境下,事務的隔離性很難保證,因此會出現很多併發一致性問題。

1. 更新丟失(Lost Update):

定義:當兩個或多個事務選擇同一行,然後基於最初選定的值更新該行時,由於每個事務都不知道其他事務的存在,就會發生丟失更新問題,最後的更新覆蓋了由其他事務所做的更新。

例如:
兩個編輯人員製作了同一文件的電子副本。每個編輯人員獨立地更改其副本,然後儲存更改後的副本,這樣就覆蓋了原始文件。 最後儲存其更改副本的編輯人員覆蓋另一個編輯人員所做的更改。如果在一個編輯人員完成並提交事務之前,另一個編輯人員不能訪問同一檔案,則可避免此問題。
T1 和 T2 兩個事務都對一個資料進行修改,T1 先修改,T2 隨後修改,T2 的修改覆蓋了 T1 的修改。

事務ACID特性與隔離級別

2. 髒讀(Dirty Reads):

定義:一個事務正在對一條記錄做修改,在這個事務完成並提交前, 這條記錄的資料就處於不一致狀態; 這時, 另一個事務也來讀取同一條記錄,如果不加控制,第二個事務讀取了這些“髒”資料,並據此做進一步的處理,就會產生未提交的資料依賴關係。這種現象被形象地叫做"髒讀"。

T1 修改一個資料,T2 隨後讀取這個資料。如果 T1 撤銷了這次修改,那麼 T2 讀取的資料是髒資料。

事務ACID特性與隔離級別

3. 不可重複讀(Non-Repeatable Reads):

定義:一個事務在讀取某些資料後的某個時間,再次讀取以前讀過的資料,卻發現其讀出的資料已經發生了改變、或某些記錄已經被刪除了!這種現象就叫做“不可重複讀” 。

T2 讀取一個資料,T1 對該資料做了修改。如果 T2 再次讀取這個資料,此時讀取的結果和第一次讀取的結果不同。

事務ACID特性與隔離級別

4. 幻讀(Phantom Reads): 

定義:一個事務按相同的查詢條件重新讀取以前檢索過的資料,卻發現其他事務插入了滿足其查詢條件的新資料,這種現象就稱為“幻讀” 。

T1 讀取某個範圍的資料,T2 在這個範圍內插入新的資料,T1 再次讀取這個範圍的資料,此時讀取的結果和第一次讀取的結果不同。

事務ACID特性與隔離級別

幻讀和不可重複讀的區別:

不可重複讀的重點是修改:在同一事務中,同樣的條件,第一次讀的資料和第二次讀的資料不一樣。(因為中間有其他事務提交了修改)
幻讀的重點在於新增或者刪除:在同一事務中,同樣的條件,,第一次和第二次讀出來的記錄數不一樣。(因為中間有其他事務提交了插入/刪除)

不可重複讀和髒讀的區別:

髒讀是讀到未提交的資料,而不可重複讀讀到的卻是已經提交的資料。

隔離級別

SQL標準定義了4類隔離級別,每一種級別都規定了一個事務中所做的修改,哪些在事務內和事務間是可見的,哪些是不可見的。低階別的隔離級別一般支援更高的併發處理,並擁有更低的系統開銷。

第1級別:Read Uncommitted(未提交讀)

所有事務都可以看到其他未提交事務的執行結果

本隔離級別很少用於實際應用,因為它的效能也不比其他級別好多少
該級別引發的問題是——髒讀(Dirty Read):讀取到了未提交的資料

第2級別:Read Committed(提交讀)

這是大多數資料庫系統的預設隔離級別(但不是MySQL預設的)
它滿足了隔離的簡單定義:

一個事務只能讀取已經提交的事務所做的修改。換句話說,一個事務所做的修改在提交之前對其它事務是不可見的。

這種隔離級別出現的問題是——不可重複讀(Nonrepeatable Read):不可重複讀意味著我們在同一個事務中執行完全相同的select語句時可能看到不一樣的結果。導致這種情況的原因可能有:

有一個交叉的事務有新的commit,導致了資料的改變;

一個資料庫被多個例項操作時,同一事務的其他例項在該例項處理期間可能會有新的commit

第3級別:Repeatable Read(可重複讀)

這是MySQL的預設事務隔離級別
保證在同一個事務中多次讀取同樣資料的結果是一樣的。
它確保同一事務的多個例項在併發讀取資料時,會看到同樣的資料行

此級別可能出現的問題——幻讀(Phantom Read):當使用者讀取某一範圍的資料行時,另一個事務又在該範圍內插入了新行,當使用者再讀取該範圍的資料行時,會發現有新的“幻影” 行
InnoDB和Falcon儲存引擎通過多版本併發控制(MVCC,Multiversion Concurrency Control)機制解決幻讀問題;InnoDB還通過間隙鎖解決幻讀問題

MVCC 並不能解決幻讀的問題,Next-Key Locks 就是為了解決這個問題而存在的。在可重複讀(REPEATABLE READ)隔離級別下,使用 MVCC + Next-Key Locks 可以解決幻讀問題。

第4級別:Serializable(可序列化)

這是最高的隔離級別,強制事務序列執行。

它通過強制事務排序,使之不可能相互衝突,從而解決幻讀問題。簡言之,它是在每個讀的資料行上加上共享鎖。
在這個級別,可能導致大量的超時現象和鎖競爭

事務ACID特性與隔離級別

小結

這篇文章給大家介紹了資料庫事務定義的ACID特性和隔離級別,以及在不同的隔離級別下分別會產生哪些併發一致性問題,更新丟失、髒讀、不可重複讀、幻讀都是我們要避免的,所幸MySQL的預設隔離級別為可重複讀,其使用MVCC+next-key locks避免了幻讀問題。其中的原理我將在下篇文章中給大家介紹。


參考自

zhuanlan.zhihu.com/p/29166694

github.com/CyC2018/CS-…


相關文章