|
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"查詢后附加“FORUPDATE”關鍵字來實現。在這種情況下,其他沖突的讀寫悲觀事務都將被阻塞直到該事務結束為止。樂觀事務會跟蹤提交時被驗證的所有修改記錄/實體的版本號。這是一種很流行的樂觀隔離級別,因此被所有的主流對象關系映射庫支持。在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服務 | 目錄 |
可重復讀取 | 允許緩沖 | 間歇的 | 對比讀入的實體版本 | 讀/寫實體 | 訂單跟蹤 |
快照隔離 | 必須被驗證 | 周期的 | 對比讀入的實體版本 | 同步實體 | 工作流 |
可串行性 | 必須被驗證 | 完整同步 | 對比查詢與修改內容 | 完善數據一致性 | 賬目 |
數據一致性在數據庫應用程序中至關重要-它允許開發者在分布式環境下使用數據。盡管強一致性級別如可串行性提供了一個簡單的編程模型,但是它們會導致開銷過大,操作阻塞或者事務回滾,這對于很多應用程序來說都是不必要的。如果有其他問題的話,可以使用更加適當的隔離級別來幫助開發人員和系統架構師,讓他們在保持性能和開銷平衡的前提下更好的理解數據一致性的需求。
it知識庫:Web開發必知的八種隔離級別,轉載需保留來源!
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。