Web開發必知的八種隔離級別

ACID性質是數據庫理論中的奠基石,它定義了一個理論上可靠數據庫所必須具備的四個性質:原子性,一致性,隔離性和持久性。雖然這四個性質都很重 要,但是隔離性最爲靈活。大部分數據庫都提供了一些可供選擇的隔離級別,且現在許多庫都增加了附加層來創建顆粒度更細的隔離。隔離級別應用範圍如此之廣主 要是因爲放寬隔離約束往往會使得可擴展性和性能提高几個數量級。

串行一致性是可用的最古老最高的隔離級別之一,它之所以倍受青睞是因爲其提供的簡單編程模型,即每次僅能有一個事務對給定的資源進行操作,這就避免 了很多潛在的資源問題。儘管如此,大部分應用程序(尤其是Web應用程序)都不採用這種級別非常高的隔離,因爲從終端用戶的角度來看這是不切實際的-任何 一個擁有大量用戶羣的應用程序在訪問共享資源時都將會有幾分鐘的延遲,而這會使得用戶量迅速減少。弱一致性和最終一致性在大規模分佈式數據源中,例如 Web中,隨處可見。好幾個成功的大型Web應用(例如,eBay和Amazon)都顯示出樂觀的(optimistic)弱一致性要比傳統悲觀的 (pessimistic)機制在擴展性方面好得多。本文將一窺八種不同的隔離級別。學會適當的放寬數據一致性的約束,你可以在自己的應用程序中使用這八 種隔離級別來獲得更好的性能和可擴展性。

併發控制的主要目標是爲了確保事務被隔離且不會影響到其他事務。要達到高級別的隔離需以犧牲性能爲代價。併發控制可以用悲觀或者樂觀的機制來實現。 大部分關係型數據庫都使用了悲觀機制來實現寫入優化。悲觀機制採用了鎖,通過使用鎖它可以阻塞一些操作或者進行某些形式的衝突檢測。當一個表格,頁面或是 行被修改後,悲觀機制中的鎖可以用來阻塞其他潛在的訪問修改資源的事務。然而,樂觀機制並不採用任何鎖,它僅僅依賴於衝突檢測來維護事務隔離。樂觀機制採 用的衝突檢測可以允許所有的讀操作,並在事務結束時檢驗其一致性。如果檢測到衝突,那麼事務會進行回滾或重做。大部分web服務器都是讀入優化,因此使用 了樂觀機制。通過允許所有的讀入操作,樂觀機制既可以保證很高的讀寫吞吐量,也可以在資源不是一直改變的情況下保證數據的一致性。

下面列出的隔離級別是用來幫助Web開發人員更好的理解他們編程模型中放置的約束,幫助系統架構師和開發人員共同討論如何在保持必要的數據完整性的同時選擇最有效的隔離級別。它們按照最少隔離(未提交讀)到最多隔離(串行化)的順序列出。

1、未提交讀(Read Uncommitted)

未提交讀隔離級別需要事務間很少的隔離。每一個讀操作都能看到事務中等待的寫操作(髒讀)。然而已經提交的寫操作必須要有一個串行順序來防止髒寫。 悲觀機制會阻塞有衝突的寫操作直到其他寫操作已經被提交或已經回滾。樂觀機制不會鎖住這些操作,它會允許所有的操作都通過。如果一個連接進行了回滾,那麼 接下來修改同一塊數據的其他操作也會被回滾。在這種級別中,共享緩衝可以不加驗證的進行使用。這種隔離級別最好在不需要事務(比如只讀的數據集),或者事 務只在獨佔數據庫時才修改的情況下使用。

例子:一個只在離線情況下更新的檔案數據庫,或者不在事務中使用的審覈/登陸(audit/logging)表。

2、已提交讀(Read Committed)

已提交讀可以讀取系統中任何已經提交的狀態,並且可以不加驗證(混合狀態)的進行緩衝,只需當前連接中發生的改變能夠反映到結果中即可。悲觀機制將 其實現爲單調視圖。樂觀事務則隔離存儲所有的改動,使得它們直到提交後纔可用。讀已提交使用一個非常樂觀的機制,它推遲寫入所有的變化直到事務被提交爲 止。這種形式的樂觀隔離可以在不阻塞讀操作的情況下實現複雜的寫入操作,並且它沒有驗證模式。共享緩衝只能在已提交的狀態中使用。這種隔離級別最好在結果 可以使用舊值,且事務只能用於寫入操作的情況下使用。

例子:一個不必顯示當前最新帖子的在線論壇,且它的帖子間數據不相沖突。

3、單調視圖(Monotonic View )

單調視圖是對讀已提交的一個擴展,它其中的事務在執行時會觀察數據庫中一個單調上升的狀態。在這種級別中,如果有明顯的寫入事務,那麼悲觀事務會在 讀入操作中被阻塞。樂觀事務會像在讀已提交中一樣操作,隔離保存所有的改動,並且會驗證它們的緩衝以確保其仍然合法。這種級別可以定期地同步數據庫副本, 且最好在不需要事務或者僅存在寫操作事務的情況下使用。

例子:一個僅能由一個人來修改的用戶偏好表。

4、快照讀取(Snapshot Reads)

快照讀取擴展了單調視圖,它可以保證查詢結果都能反映到數據庫一致的快照中。悲觀機制會在讀操作時阻礙其他影響結果的寫入操作。樂觀機制則允許其他 的寫入操作,並通知讀取事務某部分已經發生改變並進行回滾。想要實現一個樂觀機制,必須在讀操作結束之前驗證是否有什麼並行的寫入操作修改了結果,如果有 的話,那麼結果可能會重做或回滾。這個檢驗過程可能只是簡單的檢查同一張表中是否出現了寫入操作,或者只是檢查改動的查詢結果。樂觀隔離級別可以很輕鬆地 檢測出衝突,並且在允許併發讀入操作的過程中,支持寫入操作。這種級別只要能夠讀取到快照,便可以定期地同步數據庫副本。最好在寫入操作很少,不想與讀入 操作衝突,且查詢結果需要一致性的時候使用這種隔離級別。

例子::一個查詢比修改頻繁,且只保留最新值的貨幣換位表或者查詢表。

5、遊標穩定性(Cursor Stability)

遊標穩定性隔離擴展了讀已提交,並且是許多關係型數據默認的隔離級別。在這種隔離級別中,悲觀事務如果在一個單獨的語句中執行的話,必須得指定它將 修改的記錄。這通常可以在"SELECT"查詢後附加“FOR UPDATE”關鍵字來實現。在這種情況下,其他衝突的讀寫悲觀事務都將被阻塞直到該事務結束爲止。樂觀事務會跟蹤提交時被驗證的所有修改記錄/實體的版 本號。這是一種很流行的樂觀隔離級別,因此被所有的主流對象關係映射庫支持。在Java持久性API中,可以使用FLUSH_ON_COMMIT(儘管查 詢可能不影響本地改動)來接近達到這種級別,且如果檢測到衝突的話,可以拋出OptimisticLockException 異常。這種隔離也同樣可以用在HTTP頭域的If-Match或者 If-Unmodified-Since中,它可以用來在更新前對比上一個資源的版本或者時間戳。這種級別最好在實體由外部信息(不從數據庫中讀取)更 改,或者改動不會彼此覆蓋的情況下使用。

例子:一個共享的公司目錄或者一個wiki。

6、可重複讀取(Repeatable Read)

可重複讀取級別擴展了遊標穩定性,它保證事務內的任何數據在事務過程中都不會被修改或者移除。悲觀事務需要讀取所有記錄上的鎖,並阻塞其他服務來修 改這些記錄。樂觀事務則會跟蹤所有的記錄或者實體,並檢查它們是否在提交時被修改過。這種級別最好在實體狀態能夠影響其他實體,或者事務由讀寫操作構成的 情況下使用。

例子:一個訂單跟蹤數據庫,它從一個實體中讀取值並用它來計算其他的實體值。

7、快照隔離(Snapshot Isolation)

快照隔離擴展了快照讀取和可重複讀取,它保證事務中所有進行的讀操作都能看到數據庫中一致的快照。事務執行的的任何讀操作都會有相同的結果,而不管 它們在事務中執行的早晚。這和可重複讀取不同,因爲快照隔離能夠防止幻讀(查詢結果不斷變化)。許多關係型數據庫採用多版本併發控制(也可以叫做 SERIALIZABLE)來支持這種級別,實現方法是通過鎖和衝突檢測的組合。在這種級別中,考慮到它可能與悲觀機制或者樂觀機制相沖突,因此事務一定 要做好回滾的準備。悲觀機制會通過鎖住資源來嘗試減少衝突的機會,但是必須在事務提交後將這些改動合併。樂觀機制也會使用多版本併發控制,但是它不會阻塞 其他可能產生潛在衝突操作的事務,反而是將衝突的事務進行回滾。這種級別的隔離最好在事務可以讀取和修改多個記錄的情況下使用。

例子:一個基於系統狀態規則的工作流系統。

8、可串行性(Serializability)

串行性是快照隔離的擴展,它要求所有的事務都必須一個接着一個的出現,就好比它們被串行化過一樣。悲觀機制需要鎖住所有評估過的查詢,以防止寫入操 作影響這些結果。而樂觀機制則跟蹤所有評估過的查詢,並在事務結束時使用一個後向驗證或前向驗證的模式來檢查是否有並行寫入操作影響了並行讀入操作,如果 有的話,它會將衝突事務外的所有事務進行回滾。在這種隔離級別中,任何提交事務都不會改變系統的表徵狀態。最好在需要完整數據一致性的情況下使用這個級別 的隔離。

例子:一個進行範圍查詢來計算新值的賬目系統。

總結

下面是本文提到的隔離級別的彙總表,它可以幫助你找到最適合你應用程序的級別。

事務在不同隔離級別中可能的衝突類型:

  髒寫 髒讀 混合狀態 不一致讀 覆寫 不可重複 幻讀 不一致性
未提交讀 不可以 可以 可以 可以 可以 可以 可以 可以
已提交讀 不可以 不可以 可以 可以 可以 可以 可以 可以
單調視圖 不可以 不可以 不可以 可以 可以 可以 可以 可以
快照讀取 不可以 不可以 不可以 不可以d 可以 可以 可以 可以
遊標穩定性 不可以 不可以 可以 可以 不可以 可以 可以 可以
可重複讀取 不可以 不可以 可以 可以 不可以 不可以 可以 可以
快照隔離 不可以 不可以 不可以 不可以 不可以 不可以 不可以 可以
可串行性 不可以 不可以 不可以 不可以 不可以 不可以 不可以 不可以

不同隔離級別的最佳前提:

  緩衝 數據同步 樂觀衝突模式 建議操作 例子
未提交讀 允許緩衝 間歇的 檢測髒寫 不能併發讀寫 檔案
已提交讀 允許緩衝 間歇的 沒有衝突檢測 單調的讀/寫 Web論壇
單調視圖 必須被驗證 週期的 沒有衝突檢測 組合讀入 用戶偏好
快照讀取 必須被驗證 週期的 對比讀入與修改內容 一致性讀入 查詢表
遊標穩定性 允許緩衝 間歇的 對比修改的實體版本 CRUD服務 目錄
可重複讀取 允許緩衝 間歇的 對比讀入的實體版本 讀/寫實體 訂單跟蹤
快照隔離 必須被驗證 週期的 對比讀入的實體版本 同步實體 工作流
可串行性 必須被驗證 完整同步 對比查詢與修改內容 完善數據一致性 賬目

數據一致性在數據庫應用程序中至關重要-它允許開發者在分佈式環境下使用數據。儘管強一致性級別如可串行性提供了一個簡單的編程模型,但是它們會導 致開銷 過大,操作阻塞或者事務回滾,這對於很多應用程序來說都是不必要的。如果有其他問題的話,可以使用更加適當的隔離級別來幫助開發人員和系統架構師,讓他們 在保持性能和開銷平衡的前提下更好的理解數據一致性的需求。

查看英文原文:Eight Isolation Levels Every Web Developer Should Know

發佈了26 篇原創文章 · 獲贊 17 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章