【眼見爲實】數據庫併發問題 封鎖協議 隔離級別

此篇博客是【眼見爲實】系列的第一篇博客,主要從理論上講了數據庫併發可能會出現的問題,解決併發問題的技術——封鎖,封鎖約定的規則——封鎖協議。而後簡單說明了數據庫事務隔離級別和封鎖協議的對應關係。後面的幾篇博客都是經過親身實踐探究InnoDB引擎在各個隔離級別下的實現細節。html

【眼見爲實】數據庫併發問題 封鎖協議 隔離級別數據庫

【眼見爲實】本身動手實踐理解READ UNCOMMITED && SERIALIZABLE微信

【眼見爲實】本身動手實踐理解 READ COMMITTED && MVCC併發

【眼見爲實】本身動手實踐理解REPEATABLE READ && Next-Key Lock學習

數據庫併發的幾大類問題

①丟失修改(Lost Update)

兩個事務T1和T2同時讀入同一數據並修改,T2的提交的結果破壞了T1提交的結果,致使T1的修改被丟失(第二類丟失更新)。 spa

mark

還有一種特殊的丟失修改(第一類丟失更新),以下圖。由於這種丟失修改在【READ UNCOMMITED】隔離級別下都不會出現,因此不進行討論。cdn

mark

②不可重複讀(Non-Repeatable Read)

事務T1讀取數據後,事務T2執行更新操做,使事務T1沒法再現前一次讀取結果。 具體包括三種狀況: (1)事務T1讀取某一數據後,事務T2對其作了修改,當事務T1再次讀取該數據時,獲得與前一次不一樣的值。htm

mark

(2)事務T1按照必定條件讀取了某些數據記錄後,事務T2刪掉了其中部分記錄,當T1再次按相同條件查詢數據時,發現某些記錄消失了。 (3)事務T1按照必定條件讀取了某些數據記錄後,事務T2插入了一些記錄,當T1再次按相同條件查詢數據時,發現多了一些記錄。對象

##③幻讀(Phantom Read)blog

幻讀實際上是不可重複讀的一種特殊狀況。不可重複讀(2)和(3)也稱爲幻讀現象。不可重複讀是對數據的修改更新產生的;而幻讀是插入或刪除數據產生的。

mark

④讀髒數據(Dirty Read)

事務T1修改某一數據,並將其寫回磁盤,事務T2讀取同一數據後,T1由於某些緣由回滾,這時T1修改過的數據恢復原值,T2讀取到的數據就與數據庫中的數據不一致,則T2讀取到數據就爲「髒數據「,即不正確的數據。

mark

併發控制的主要技術是封鎖

基本封鎖類型①排它鎖(Exclusive Locks,簡稱X鎖) 排它鎖又稱爲寫鎖。若事務T對數據對象A加上X鎖,則只容許T修改和讀取A,其餘任何事務都不能再對A加任何類型的鎖,直到T釋放A上的鎖。這就保證了其餘事務在T釋放A上的鎖以前都不能再讀取和修改A。 ②共享鎖(Share Locks,簡稱S鎖) 共享鎖又稱爲讀鎖。若事務T對數據對象A加上S鎖,則事務T能夠讀取A但不能修改A。其餘事務只能再對A加S鎖,而不能加X鎖,直到T釋放A上的S鎖。這就保證了其餘事務能夠讀取A,可是在T釋放A上的S鎖以前不能對A作任何修改。

排它鎖與共享鎖的相容矩陣

mark

封鎖協議

在運用X鎖和S鎖這兩種基本封鎖,對數據對象加鎖時,還須要約定一些規則。例如什麼時候申請X鎖和S鎖,持鎖時間,什麼時候釋放等。這些規格稱爲封鎖協議。

一級封鎖協議

一級封鎖協議:事務T在修改數據A以前必須對其加X鎖,直到事務結束才釋放。事務結束包括正常結束(Commit)和非正常結束(RollBack)一級封鎖協議可防止丟失修改。 使用一級封鎖協議解決了圖1中的覆蓋丟失問題。事務T1在讀A進行修改以前先對A加X鎖,當T2再請求對A加X鎖時被拒絕,T2只能等待T1釋放A上的鎖後T2得到A上的X鎖,這時它讀取的A已是T1修改後的15,再按照此值進行計算,將結果值A=14寫入磁盤。這樣就避免了丟失T1的更新。

mark

二級封鎖協議

二級封鎖協議:一級封鎖協議加上事務T在讀取數據A以前必須先對其加S鎖,讀完後便可釋放S鎖二級封鎖協議除防止了丟失修改,還進一步防止了讀「髒」數據。 使用二級封鎖協議解決了圖2中的髒讀問題。事務T1在讀C進行修改以前先對C加X鎖,修改其值後寫回磁盤。這時T2請求在C上加S鎖,由於T1在C上已經加了X鎖,因此T2只能等待。T1由於某種緣由被撤銷,C恢復原值100。T1釋放C上的X鎖後T2得到C上的S鎖,讀C=100。這樣就避免了讀「髒」數據。

mark

三級封鎖協議

三級封鎖協議:一級封鎖協議加上事務T在讀取數據A以前必須先對其加S鎖,直到事務結束才釋放三級封鎖協議除防止了丟失修改和讀「髒」數據,還進一步防止了不可重複讀。 使用三級封鎖協議解決了圖3中的不可重複讀問題。事務T1在讀取數據A和數據B以前對其加S鎖,其餘事務只能再對A、B加S鎖,不能加X鎖,這樣其餘事務只能讀取A、B,而不能更改A、B。這時T2請求在B上加X鎖,由於T1已經在B上加了S鎖,因此T2只能等待。T1爲了驗算結果再次讀取A、B的值,由於其餘事務沒法修改A、B的值,因此結果仍然爲150,便可重複讀。此時T1釋放A、B上的S鎖,T2纔得到B上的X鎖。這樣就避免了不可重複讀。

mark

活鎖和死鎖

封鎖可能會引發活鎖活死鎖。

活鎖

若是事務T1封鎖了數據R,事務T2又請求封鎖數據R,因而T2等待。事務T3也請求封鎖R,當事務T1釋放了數據R上的封鎖以後系統首先批准了事務T3的封鎖請求,T2仍然等待。而後T4又申請封鎖R,當T3釋放了R的封鎖以後系統又批准了T4的封鎖請求。T2有可能一直等待下去,這就是活鎖。

mark

避免活鎖的方法就是先來先服務的策略。當多個事務請求對同一數據對象封鎖時,封鎖子系統按照請求的前後對事務排隊。數據對象上的鎖一旦釋放就批准申請隊列中的第一個事務得到鎖。

##死鎖

若是事務T1封鎖了數據R1,事務T2封鎖了數據R2,而後T1又請求封鎖數據R2,由於T2已經封鎖了數據R2,因而T1等待T2釋放R2上的鎖。接着T2又申請封鎖R1,由於由於T1已經封鎖了數據R1,T2也只能等待T1釋放R1上的鎖。這樣就出現了T1在等待T2,T2也在等待T1的局面,T1和T2兩個事務永遠不能結束,造成死鎖。

mark

死鎖的預防

①一次封鎖法

一次封鎖法要求事務必須一次將全部要使用的數據所有加鎖,不然不能繼續執行。例如上圖中的事務T1將數據R1和R2一次加鎖,T1就能執行下去,而T2等待。T1執行完成以後釋放R1,R2上的鎖,T2繼續執行。這樣就不會產生死鎖。

一次封鎖法雖然能防止死鎖的發生,可是缺點卻很明顯。一次性將之後要用到的數據加鎖,勢必擴大了封鎖的範圍 ,從而下降了系統的併發度。

②順序封鎖法

順序封鎖法是預先對數據對象規定一個封鎖順序,全部的事務都按照這個順序實行封鎖。

順序封鎖法雖然能夠有效避免死鎖,可是問題也很明顯。第一,數據庫系統封鎖的數據對象極多,而且隨着數據的插入、刪除等操做不斷變化,要維護這樣的資源的封鎖順序很是困難,成本很高。第二,事務的封鎖請求能夠隨着事務的執行動態的肯定,所以很難按照規定的順序實行封鎖。

可見,預防死鎖的產生並非很適合數據庫的特色,因此在解決死鎖的問題上廣泛採用的是診斷而且解除死鎖。

死鎖的診斷與解除

①超時法

若是一個事務的等待時間超過了默認的時間,就認爲是產生了死鎖。

②等待圖法

一旦檢測到系統中存在死鎖就要設法解除。一般的解決方法是選擇一個處理死鎖代價最小的事務,將其撤銷,釋放此事務持有的全部的鎖,恢復其所執行的數據修改操做,使得其餘事務得以運行下去。

兩段鎖協議

所謂的二段鎖協議是指全部事務必須分兩個階段對數據進行加鎖和解鎖操做。

  • 在對任何數據進行讀、寫操做以前,首先要申請並得到該數據的封鎖。

  • 在釋放一個封鎖以後,事務不在申請和得到其餘封鎖。

也就是說事務分爲兩個階段。第一個階段是得到封鎖,也稱爲擴展階段。在這個階段,事務能夠申請得到任何數據項任何類型的鎖,可是不能釋聽任何鎖。第二階段是釋放封鎖,也稱爲收縮階段。在這個階段,事務能夠釋聽任何數據項上任何類型的封鎖,可是不能再申請任何鎖。

事務遵照兩段鎖協議是可串行化調度的充分條件,而不是必要條件。也就是說遵照兩段鎖協議必定是可串行化調度的,而可串行化調度的不必定是遵照兩段鎖協議的。

mark
左側T一、T2遵循兩段鎖協議,右側T一、T2並不遵循兩段鎖協議

兩段鎖協議和一次封鎖法的異同

一次封鎖法要求事務必須將要使用的數據所有加鎖,不然不能繼續執行。所以一次封鎖法遵照兩段鎖協議。

可是兩段鎖協議並不要求事務將要使用的數據一次所有加鎖,所以兩段鎖協議可能發生死鎖。如圖:

mark

數據庫隔離級別

封鎖協議和隔離級別並非嚴格對應的

各類隔離級別所能避免的併發問題

mark


做者: 擼碼那些事
聲明:本文爲博主學習感悟總結,水平有限,若是不當,歡迎指正。若是您認爲還不錯,不妨關注一下下方的 微信公衆號,謝謝支持。轉載與引用請註明出處。
相關文章
相關標籤/搜索