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

sunnylovecmc發表於2010-01-17

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服務  目錄 
可重複讀取 允許緩衝  間歇的 對比讀入的實體版本 讀/寫實體  訂單跟蹤 
快照隔離 必須被驗證  週期的  對比讀入的實體版本 同步實體  工作流 
可序列性 必須被驗證  完整同步  對比查詢與修改內容  完善資料一致性  賬目 

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


本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/shadowkiss/archive/2009/11/19/4834800.aspx

相關文章