並發控制
並發控制的任務: 對並發操作進行正確調度(可串行化調度)
保證事務隔離度
保證數據庫一致性
並發操作帶來的不一致性:丟失修改 不可重復讀(包括幻讀) 臟讀
1.丟失修改
兩個事務同時更新一行數據,最后一個事務的更新會覆蓋掉第一個事務的更新,從而導致第一個事務更新的數據丟失,這是由於沒有加鎖造成的;
2.臟讀
臟讀就是指當一個事務正在訪問數據,並且對數據進行了修改,而這種修改還沒有提交到數據庫中,這時,另外一個事務也訪問這個數據,然后使用了這個數據。
3.不可重復讀
是指在一個事務內,多次讀同一數據。在這個事務還沒有結束時,另外一個事務也訪問該同一數據。那么,在第一個事務中的兩次讀數據之間,由於第二個事務的修改,那么第一個事務兩次讀到的的數據可能是不一樣的。
這樣在一個事務內兩次讀到的數據不一樣,因此稱為是不可重復讀。
4.幻讀
是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的數據進行了修改,這種修改涉及到表中的全部數據行。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那么,以后就會發生操作第一個事務的用戶發現表中還有沒有修改的數據行,就好象發生了幻覺一樣。
不可重復讀的重點是修改(1讀--2修--1讀):
同樣的條件, 你讀取過的數據, 再次讀取出來發現值不一樣了
幻讀的重點在於新增或者刪除(1修--2增/刪--1讀)
同樣的條件, 第1次和第2次讀出來的記錄數不一樣
這里,似乎不可重復讀和幻影讀是一致的,那么它們的區別是什么呢?
從控制的角度來講,不可重復讀只需要鎖住滿足條件的記錄,而幻影讀要鎖住滿足條件的及其相近的記錄。所以,避免幻讀,必須鎖住表,避免不可重復讀,只需要鎖住行。
參考https://www.iteye.com/blog/uule-1109647
事務隔離等級
未提交讀(READ UNCOMMITED):兩個事務互相可見,即使一個事務未提交也能獲取其使用的數據
已提交讀(READ COMMITED):一個事務進行時可見其他已提交的事務
可重復讀(REPEATABLE READ):事務進行時,其他所有事務都對其不可見,無法獲取其他事務的任何數據,也是InnoDB的默認隔離等級
可串行化(SERIALIZABLE):在讀取的每一行數據上都加上鎖,會造成大量的鎖超時和鎖征用,嚴格保證一致性,不存在並發
封鎖
排它鎖(Exclusive Lock,X鎖):不允許其他事務讀、修改、加鎖
共享鎖(Share Lock,S鎖):允許其他事務讀A、加S鎖,但在S鎖釋放前不可修改
封鎖協議
一級封鎖協議:事務在修改數據前必須先加X鎖,直到事務結束釋放鎖
一級封鎖防止修改丟失,保證事務的可恢復
由於不修改就不用加鎖,不能防止臟讀
二級封鎖協議:一級封鎖協議+ 事務在讀取數據前必須加S鎖,讀完釋放
二級封鎖避免了修改丟失、臟讀,但存在不可重復讀
三級封鎖協議:一級封鎖協議+ 事務在讀取數據前必須加S鎖,事務結束釋放
三級封鎖協議可防止修改丟失、臟讀、不可重復讀
封鎖協議可能帶來新的問題:活鎖和死鎖
活鎖
事務T1封鎖了數據R
事務T2又請求封鎖R,於是T2等待。
T3也請求封鎖R,當T1釋放了R上的封鎖之后系統首先批准了T3的請求,T2仍然等待。
T4又請求封鎖R,當T3釋放了R上的封鎖之后系統又批准了T4的請求……
T2有可能永遠等待,這就是活鎖的情形
解決方法:先來先服務策略
死鎖
事務T1封鎖了數據R1
T2封鎖了數據R2
T1又請求封鎖R2,因T2已封鎖了R2,於是T1等待T2釋放R2上的鎖
接着T2又申請封鎖R1,因T1已封鎖了R1,T2也只能等待T1釋放R1上的鎖
這樣T1在等待T2,而T2又在等待T1,T1和T2兩個事務永遠不能結束,形成死鎖
預防方法:一次封鎖法,必須一次對所有要使用的數據全部加鎖,但難確定一個事務會使用到哪些數據
順序封鎖法,對數據對象規定一個封鎖順序,事務按照順序進行封鎖,但維護成本高也難以實現
數據庫解決死鎖更多是通過
診斷並解除死鎖:超時法,一個事務若超時,就認為發生了死鎖
但有可能誤判長事務或時限設置太長不能及時發現
等待圖法,事務等待圖中是否存在回路來判斷發生死鎖,撤銷代價最小的事務釋放其鎖
並發調度的可串行化
一個調度是可串行化的才認為是正確的調度,可串行化就是調度的結果等價於一種(A、B的順序可能對換)串行執行的結果
沖突可串行化調度
沖突操作指的是不同事物對同一數據的讀-寫和寫-寫
在調度序列中,不同事物的沖突操作、同一事務的兩個操作,不能交換(SWAP)
今有調度Sc1=r1(A)w1(A)r2(A)w2(A)r1(B)w1(B)r2(B)w2(B)
把w2(A)與r1(B)w1(B)交換,得到:
r1(A)w1(A)r2(A)r1(B)w1(B)w2(A)r2(B)w2(B)
再把r2(A)與r1(B)w1(B)交換:
Sc2=r1(A)w1(A)r1(B)w1(B)r2(A)w2(A)r2(B)w2(B)
Sc2等價於一個串行調度T1,T2,Sc1沖突可串行化的調度
一個調度Sc在保證沖突操作次序不變的情況下,交換不沖突操作得到一個串行調度,那么稱其是沖突可串行化的
沖突可串行化是在可串行化上加了條件
有3個事務
T1=W1(Y)W1(X),T2=W2(Y)W2(X),T3=W3(X)
調度L1=W1(Y)W1(X)W2(Y)W2(X) W3(X)是一個串行調度。
調度L2=W1(Y)W2(Y)W2(X)W1(X)W3(X)不滿足沖突可串行化。
但是調度L2是可串行化的,因為L2執行的結果與調度L1相同,Y的值都等於T2的值,X的值都等於T3的值
兩段鎖協議
事務分為兩個階段
①擴展階段
事務只能申請鎖,不能釋放鎖
②收縮階段
事務只能釋放鎖,不能申請鎖
遵循兩段鎖協議必定是可串行化的
兩段鎖和一次封鎖法的區別:
預防死鎖的一次封鎖法遵守兩段鎖協議;但是兩段鎖協議並不要求事務必須一次將所有要使用的數據全部加鎖,因此遵守兩段鎖協議的事務可能發生死鎖。
原因在於兩段鎖協議僅是針對一個事務的加鎖、釋放時期,死鎖則是針對兩個事務的並發過程中的搶鎖問題
封鎖粒度
封鎖對象的大小稱為封鎖粒度(Granularity)
邏輯單元:屬性值,屬性值集,元組,關系,索引項,整個索引,整個數據庫
物理單元:頁(數據頁,索引頁),物理記錄
封鎖粒度與系統的並發度和並發控制的開銷密切相關:
1.封鎖的粒度越大,數據庫所能夠封鎖的數據單元就越少,並發度就越小,系統開銷也越小;
2 .封鎖的粒度越小,並發度較高,但系統開銷也就越大
選擇封鎖粒度:
1.需要處理多個關系的大量元組的用戶事務:以數據庫為封鎖單位
2.需要處理大量元組的用戶事務:以關系為封鎖單元
3.只處理少量元組的用戶事務:以元組為封鎖單位
多粒度封鎖:在一個系統中同時支持多種封鎖粒度供事務選擇
多粒度樹中,根節點是整個數據庫,表示最大的數據粒度。葉節點是最小的,如元組、屬性值
在多粒度封鎖中,一個數據對象能被分為顯式封鎖(本身直接加鎖)和隱式封鎖(上級結點加鎖)
意向鎖(Intention Lock)
在上述多粒度封鎖中,對某個數據加鎖要檢查數據對象本身、上級、下級結點的封鎖情況,為了提高檢查效率采用意向鎖
對一個結點加意向鎖,說明該結點下層正在加鎖
對任一結點加鎖,必須先對其上層結點加意向鎖
①意向共享鎖(IS)
表示該結點的后裔結點擬(意向)加S鎖
②意向排它鎖(IX)
表示該節點的后裔結點擬(意向)加X鎖
③共享意向排它鎖(SIX=S+IX)
表示對其本身加S鎖,再加IX表示后裔結點擬加X鎖
例如對某個表加SIX鎖,則表示該事務要讀(S)整個表,同時會更新(IX)個別元組。
鎖的強度
指對其他鎖的排斥程度
一個事務在申請封鎖時,以強鎖代替弱鎖是安全的,反之不然。
強度排序:
X > SIX > S / IX > IS
具有意向鎖的多粒度封鎖方法
在具有意向鎖的多粒度封鎖方法中,任意事務T要對一個數據對象加鎖,必須先對它的上層結點加意向鎖。
申請封鎖時應該按自上而下的次序進行,釋放封鎖時則應該按自下而上的次序進行。(棧結構)