譯 原文連結:https://vladmihalcea.com/a-beginners-guide-to-acid-and-database-transactions/
@
介紹
事務在當今的企業系統中無處不在,在高度併發的環境中也可以提供資料一致性。因此,讓我們首先了解相關的名詞以及在什麼樣的場景使用它。
事務是讀/寫操作的集合,僅當所有包含的操作都成功時才成功。
事務具有四個特性(通常稱為ACID):
- Atomicity(原子性)
- Consistency(一致性)
- Isolation(隔離性)
- Durability(永續性)
在關聯式資料庫中,每個SQL語句必須在事務範圍內執行。在不顯式定義事務範圍的情況下,資料庫將使用隱式事務,隱式事務環繞每個單獨的語句。隱式事務在語句執行之前開始,在語句執行之後結束(提交或回滾)。隱式事務模式通常稱為自動提交。
對於企業應用程式,通常會希望避免使用自動提交模式,因為它會嚴重影響效能,並且不允許你在單個原子工作單元中包含多個DML操作。
Atomicity(原子性)
原子性把單個操作作為一個整體並將其轉變為全部成功或全部失敗的單元,只有當所有包含的操作都成功時,該操作才能成功。
事務可能封裝狀態的更改(除非它是隻讀)。無論在任何給定時間有多少個併發事務,必須使系統始終保持狀態一致。
Consistency(一致性)
一致性意味著對每個已提交的事務強制執行一致性約束。也就是說所有鍵,資料型別,檢查和觸發器均成功,並且不會觸發任何一致性衝突。
Isolation(隔離性)
事務需要併發控制機制,即使在被交錯時也能保證正確性。隔離給我們帶來的好處是隔離未提交的事務更改狀態,失敗的事務永遠都不會影響當前事務的狀態。通過使用悲觀鎖或樂觀鎖機制的併發控制來實現隔離。
Durability(永續性)
成功的事務必須永久更改系統的狀態,並且在系統停止之前,將狀態更改記錄在持久事務日誌中。如果突然系統崩潰或斷電,那麼所有未完成的已提交事務都可能會被重放。
對於JMS這樣的訊息傳遞系統,事務不是強制性的。這就是我們擁有無事務確認模式的原因。
檔案系統操作通常是無事務管理的,但是如果你的業務需求需要對檔案進行事務操作,則可以使用XADisk之類的工具。
儘管訊息傳遞和檔案系統可選擇性地使用事務,但是對於資料庫管理系統,事務是強制性。
挑戰
ACID是一個久遠的說法。吉姆·格雷(Jim Gray)在我出生之前就已經描述了原子性,一致性和永續性。但是那篇論文沒有提到隔離性。如果我們想到70年代後期的資料庫管理系統,這是可以理解的,吉姆·格雷說:
“目前,最大的航空公司和銀行在任何時刻都有大約10,000個操作和大約100個活躍的事務”。
因此,主要的付出都花在保證交付正確性上,而不是併發上。從那個時候到現在,情況發生了翻天覆地的變化,如今,即使是較低的設定也有1000 TPS。
從資料庫的角度來看,原子性是固定屬性,但是出於效能/可伸縮性的考慮,其它的特性都需要權衡。
如果資料庫系統由多個節點組成,則分散式系統一致性(CAP定理中的C,而不是ACID中的C)要求將所有更改都同步到所有節點(多主從複製)。如果副本節點是非同步更新的,那麼我們將違反一致性規則,系統將“最終保持一致”。
彼得·貝利斯(Peter Bailis)的一篇很好的文章解釋了CAP定理中的一致性和ACID中的一致性之間的區別。
事務是資料狀態轉換,因此即使所有事務同時執行,系統也必須像所有事務都是以序列形式發生一樣進行操作。
如果始終只有一個連線執行,那麼序列將不會增加任何併發控制成本。實際上,所有事務系統都必須相容併發請求,因此序列化會影響可伸縮性。阿姆達爾定律描述了序列執行與併發之間的關係:
“在平行計算中使用多個處理器的程式的速度受到程式順序部分所需時間的限制。”
稍後你將看到,大多數資料庫管理系統都選擇(預設情況下)放寬資料正確性的要求,以實現更好的併發性。
如果企業系統業務需求不要求永續性事務,那麼對於高效能叢集資料庫來說,永續性發揮作用才有意義。但是,大多數情況下,永續性最好保持不變。
隔離級別
儘管某些資料庫管理系統提供了MVCC,但通常併發控制是通過鎖來實現的。但是眾所周知,鎖會增加執行程式碼的可序列化部分,從而影響並行效率。
SQL標準定義了四個隔離級別:
- 讀取未提交(READ_UNCOMMITTED)
- 讀取已提交(READ_COMMITTED)
- 可重複讀(REPEATABLE_READ)
- 序列化(Serializable)
除序列化級別外,其他所有級別都可能受到資料異常的影響,不同級別可能發生的資料異常現象如下:
隔離級別 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
讀取未提交 | 允許 | 允許 | 允許 |
讀取已提交 | 阻止 | 允許 | 允許 |
可重複讀 | 阻止 | 阻止 | 允許 |
序列化 | 阻止 | 阻止 | 阻止 |
異常現象
但是,我們剛剛列出的所有異常現象是什麼?我們對每一個進行討論。
Dirty read(髒讀)
當允許事務讀取其他正在執行的事務的未提交更改時,就會發生髒讀。發生這種情況是因為沒有鎖阻止它。在上圖中,您可以看到第二個事務使用了不一致的值,因為第一個事務已回滾。
有關“髒讀”異常的更多資訊,請檢視這篇文章。
Non-repeatable read(不可重複讀)
所謂不可重複讀,由於併發事務剛剛更新了我們正在讀取的記錄,因此連續讀取產生不同的結果。這是我們不想要的,因為最終使用了過時的資料。通過在當前事務的整個持續時間內在讀取記錄上保留一個共享鎖(讀鎖),可以防止這種情況。
有關不可重複讀取異常的更多資訊,請檢視這篇文章。
Phantom read(幻讀)
當後續的事務插入了資料,剛好插入的資料又能被並行的事務先前的查詢查到,就會發生幻讀。因此,我們最終將使用過時的資料,這可能會影響我們的業務執行。使用範圍鎖或謂詞鎖可以防止這種情況。
有關幻讀異常的更多資訊,請檢視這篇文章。
其它異常現象
即使在SQL標準中未提及,你也應注意其它的現象,例如:
- 丟失更新
- 讀取偏差
- 寫入偏差
知道何時會發生這些現象就可以正確地解決它們,這就是資料完整性的全部意義所在。
預設隔離級別
即使SQL標準要求使用SERIALIZABLE隔離級別,但大多數資料庫管理系統使用不同的預設級別。
資料庫 | 隔離級別 |
---|---|
Oracle | 讀取已提交 |
MySQL | 可重複讀 |
Microsoft SQL Server | 讀取已提交 |
PostgreSQL | 讀取已提交 |
DB2 | CURSOR STABILITY |
總結
通常,READ COMMITTED是合適的選擇,因為即使SERIALIZABLE都不能保護你免受丟失更新的影響,在更新丟失的情況下,讀/寫發生在不同的事務(和Web請求)中。你應該考慮你的系統要求,並進行測試以確定哪個隔離級別最適合你的需求。
博主水平有限,難免錯漏,歡迎指出,或直接檢視原文!