ACID性質是數據庫理論中的奠定石,它定義了一 個理論上可靠數據庫所必須具有的四個性質:原子性,一致性,隔離性和持久性。雖然這四個性質都很 重要,可是隔離性最爲靈活。大部分數據庫都提供了一些可供選擇的隔離級別,且如今許多庫都增長了附加層來建立顆粒度更細的隔離。隔離級別應用範圍如此之廣 主要是由於放寬隔離約束每每會使得可擴展性和性能提升幾個數量級。 web
併發控制的主要目標是爲了確保事務被隔離且不會影響到其餘事務。要達到高級別的隔離需以犧牲性能爲代價。併發控制能夠用悲觀或者樂觀的機制來實現。 大部分關係型數據庫都使用了悲觀機制來實現寫入優化。悲觀機制採用了鎖,經過使用鎖它能夠阻塞一些操做或者進行某些形式的衝突檢測。當一個表格,頁面或是 行被修改後,悲觀機制中的鎖能夠用來阻塞其餘潛在的訪問修改資源的事務。然而,樂觀機制並不採用任何鎖,它僅僅依賴於衝突檢測來維護事務隔離。樂觀機制採 用的衝突檢測能夠容許全部的讀操做,並在事務結束時檢驗其一致性。若是檢測到衝突,那麼事務會進行回滾或重作。大部分web服務器都是讀入優化,所以使用 了樂觀機制。經過容許全部的讀入操做,樂觀機制既能夠保證很高的讀寫吞吐量,也能夠在資源不是一直改變的狀況下保證數據的一致性。串行一致性是可用的最古 老最高的隔離級別之一,它之因此倍受青睞是由於其提供的簡單編程模型,即每次僅能有一個事務對給定的資源進行操做,這就避免了不少潛在的資源問題。儘管如 此,大部分應用程序(尤爲是Web應用程序)都不採用這種級別很是高的隔離,由於從終端用戶的角度來看這是不切實際的-任何一個擁有大量用戶羣的應用程序 在訪問共享資源時都將會有幾分鐘的延遲,而這會使得用戶量迅速減小。弱一致性和最終一致性在大規模分佈式數據源中,例如Web中,隨處可見。好幾個成功的 大型Web應用(例如,eBay和Amazon)都顯示出樂觀的(optimistic)弱一致性要比傳統悲觀的(pessimistic)機制在擴展性 方面好得多。本文將一窺八種不一樣的隔離級別。學會適當的放寬數據一致性的約束,你能夠在本身的應用程序中使用這八種隔離級別來得到更好的性能和可擴展性。 數據庫
下面列出的隔離級別是用來幫助Web開發人員更好的理解他們編程模型中放置的約束,幫助系統架構師和開發人員共同討論如何在保持必要的數據完整性的 同時選擇最有效的隔離級別。它們按照最少隔離(未提交讀)到最多隔離(串行化)的順序列出。編程
未提交讀隔離級別須要事務間不多的隔離。每個讀操做都能看到事務中等待的寫操做(髒讀)。然而已經提交的寫操做必需要有一個串行順序來防止髒寫。 悲觀機制會阻塞有衝突的寫操做直到其餘寫操做已經被提交或已經回滾。樂觀機制不會鎖住這些操做,它會容許全部的操做都經過。若是一個鏈接進行了回滾,那麼 接下來修改同一塊數據的其餘操做也會被回滾。在這種級別中,共享緩衝能夠不加驗證的進行使用。這種隔離級別最好在不須要事務(好比只讀的數據集),或者事 務只在獨佔數據庫時才修改的狀況下使用。服務器
例子 :一個只在離線狀況下更新的檔案數據庫,或者不在事務中使用的審覈/登錄(audit/logging) 表。架構
已提交讀能夠讀取系統中任何已經提交的狀態,而且能夠不加驗證(混合狀態)的進行緩衝,只需當前鏈接中發生的改變可以反映到結果中便可。悲觀機制將 其實現爲單調視圖。樂觀事務則隔離存儲全部的改動,使得它們直到提交後纔可用。讀已提交使用一個很是樂觀的機制,它推遲寫入全部的變化直到事務被提交爲 止。這種形式的樂觀隔離能夠在不阻塞讀操做的狀況下實現複雜的寫入操做,而且它沒有驗證模式。共享緩衝只能在已提交的狀態中使用。這種隔離級別最好在結果 可使用舊值,且事務只能用於寫入操做的狀況下使用。併發
例子 :一個沒必要顯示當前最新帖子的在線論壇,且它的帖子間數據不相沖突。分佈式
單調視圖是對讀已提交的一個擴展,它其中的事務在執行時會觀察數據庫中一個單調上升的狀態。在這種級別中,若是有明顯的寫入事務,那麼悲觀事務會在 讀入操做中被阻塞。樂觀事務會像在讀已提交中同樣操做,隔離保存全部的改動,而且會驗證它們的緩衝以確保其仍然合法。這種級別能夠按期地同步數據庫副本, 且最好在不須要事務或者僅存在寫操做事務的狀況下使用。性能
例子 :一個僅能由一我的來修改的用戶偏好表。優化
快照讀取擴展了單調視圖,它能夠保證查詢結果都能反映到數據庫一致的快照中。悲觀機制會在讀操做時阻礙其餘影響結果的寫入操做。樂觀機制則容許其餘 的寫入操做,並通知讀取事務某部分已經發生改變並進行回滾。想要實現一個樂觀機制,必須在讀操做結束以前驗證是否有什麼並行的寫入操做修改告終果,若是有 的話,那麼結果可能會重作或回滾。這個檢驗過程可能只是簡單的檢查同一張表中是否出現了寫入操做,或者只是檢查改動的查詢結果。樂觀隔離級別能夠很輕鬆地 檢測出衝突,而且在容許併發讀入操做的過程當中,支持寫入操做。這種級別只要可以讀取到快照,即可以按期地同步數據庫副本。最好在寫入操做不多,不想與讀入 操做衝突,且查詢結果須要一致性的時候使用這種隔離級別。spa
例子 ::一個查詢比修改頻繁,且只保留最新值的貨幣換位表或者查詢表。
遊標穩定性隔離擴展了讀已提交,而且是許多關係型數據默認的隔離級別。在這種隔離級別中,悲觀事務若是在一個單獨的語句中執行的話,必須得指定它將 修改的記錄。這一般能夠在」SELECT」查詢後附加「FOR UPDATE」關鍵字來實現。在這種狀況下,其餘衝突的讀寫悲觀事務都將被阻塞直到該事務結束爲止。樂觀事務會跟蹤提交時被驗證的全部修改記錄/實體的版 本號。這是一種很流行的樂觀隔離級別,所以被全部的主流對象關係映射庫支持。在Java持久性API中,可使用FLUSH_ON_COMMIT(儘管查 詢可能不影響本地改動)來接近達到這種級別,且若是檢測到衝突的話,能夠拋出OptimisticLockException 異常。這種隔離也一樣能夠用在HTTP頭域的If-Match或者 If-Unmodified-Since中,它能夠用來在更新前對比上一個資源的版本或者時間戳。這種級別最好在實體由外部信息(不從數據庫中讀取)更 改,或者改動不會彼此覆蓋的狀況下使用。
例子 :一個共享的公司目錄或者一個wiki。
可重複讀取級別擴展了遊標穩定性,它保證事務內的任何數據在事務過程當中都不會被修改或者移除。悲觀事務須要讀取全部記錄上的鎖,並阻塞其餘服務來修 改這些記錄。樂觀事務則會跟蹤全部的記錄或者實體,並檢查它們是否在提交時被修改過。這種級別最好在實體狀態可以影響其餘實體,或者事務由讀寫操做構成的 狀況下使用。
例子 :一個訂單跟蹤數據庫,它從一個實體中讀取值並用它來計算其餘的實體值。
快照隔離擴展了快照讀取和可重複讀取,它保證事務中全部進行的讀操做都能看到數據庫中一致的快照。事務執行的的任何讀操做都會有相同的結果,而無論 它們在事務中執行的遲早。這和可重複讀取不一樣,由於快照隔離可以防止幻讀(查詢結果不斷變化)。許多關係型數據庫採用多版本併發控制(也能夠叫作 SERIALIZABLE)來支持這種級別,實現方法是經過鎖和衝突檢測的組合。在這種級別中,考慮到它可能與悲觀機制或者樂觀機制相沖突,所以事務必定 要作好回滾的準備。悲觀機制會經過鎖住資源來嘗試減小衝突的機會,可是必須在事務提交後將這些改動合併。樂觀機制也會使用多版本併發控制,可是它不會阻塞 其餘可能產生潛在衝突操做的事務,反而是將衝突的事務進行回滾。這種級別的隔離最好在事務能夠讀取和修改多個記錄的狀況下使用。
例子 :一個基於系統狀態規則的工做流系統。
串行性是快照隔離的擴展,它要求全部的事務都必須一個接着一個的出現,就比如它們被串行化過同樣。悲觀機制須要鎖住全部評估過的查詢,以防止寫入操 做影響這些結果。而樂觀機制則跟蹤全部評估過的查詢,並在事務結束時使用一個後向驗證或前向驗證的模式來檢查是否有並行寫入操做影響了並行讀入操做,若是 有的話,它會將衝突事務外的全部事務進行回滾。在這種隔離級別中,任何提交事務都不會改變系統的表徵狀態。最好在須要完整數據一致性的狀況下使用這個級別 的隔離。
例子 :一個進行範圍查詢來計算新值的帳目系統。
下面是本文提到的隔離級別的彙總表,它能夠幫助你找到最適合你應用程序的級別。
事務在不一樣隔離級別中可能的衝突類型:
髒寫 | 髒讀 | 混合狀態 | 不一致讀 | 覆寫 | 不可重複 | 幻讀 | 不一致性 | |
未提交讀 | 不能夠 | 能夠 | 能夠 | 能夠 | 能夠 | 能夠 | 能夠 | 能夠 |
已提交讀 | 不能夠 | 不能夠 | 能夠 | 能夠 | 能夠 | 能夠 | 能夠 | 能夠 |
單調視圖 | 不能夠 | 不能夠 | 不能夠 | 能夠 | 能夠 | 能夠 | 能夠 | 能夠 |
快照讀取 | 不能夠 | 不能夠 | 不能夠 | 不能夠d | 能夠 | 能夠 | 能夠 | 能夠 |
遊標穩定性 | 不能夠 | 不能夠 | 能夠 | 能夠 | 不能夠 | 能夠 | 能夠 | 能夠 |
可重複讀取 | 不能夠 | 不能夠 | 能夠 | 能夠 | 不能夠 | 不能夠 | 能夠 | 能夠 |
快照隔離 | 不能夠 | 不能夠 | 不能夠 | 不能夠 | 不能夠 | 不能夠 | 不能夠 | 能夠 |
可串行性 | 不能夠 | 不能夠 | 不能夠 | 不能夠 | 不能夠 | 不能夠 | 不能夠 | 不能夠 |
不一樣隔離級別的最佳前提:
緩衝 | 數據同步 | 樂觀衝突模式 | 建議操做 | 例子 | |
未提交讀 | 容許緩衝 | 間歇的 | 檢測髒寫 | 不能併發讀寫 | 檔案 |
已提交讀 | 容許緩衝 | 間歇的 | 沒有衝突檢測 | 單調的讀/寫 | Web論壇 |
單調視圖 | 必須被驗證 | 週期的 | 沒有衝突檢測 | 組合讀入 | 用戶偏好 |
快照讀取 | 必須被驗證 | 週期的 | 對比讀入與修改內容 | 一致性讀入 | 查詢表 |
遊標穩定性 | 容許緩衝 | 間歇的 | 對比修改的實體版本 | CRUD服務 | 目錄 |
可重複讀取 | 容許緩衝 | 間歇的 | 對比讀入的實體版本 | 讀/寫實體 | 訂單跟蹤 |
快照隔離 | 必須被驗證 | 週期的 | 對比讀入的實體版本 | 同步實體 | 工做流 |
可串行性 | 必須被驗證 | 完整同步 | 對比查詢與修改內容 | 完善數據一致性 | 帳目 |
數據一致性在數據庫應用程序中相當重要-它容許開發者在分佈式環境下使用數據。儘管強一致性級別如可串行性提 供了一個簡單的編程模型,可是它們會致使開銷 過大,操做阻塞或者事務回滾,這對於不少應用程序來講都是沒必要要的。若是有其餘問題的話,可使用更加適當的隔離級別來幫助開發人員和系統架構師,讓他們 在保持性能和開銷平衡的前提下更好的理解數據一致性的需求。
查看英文原文: Eight Isolation Levels Every Web Developer Should Know 。