封鎖的類型以及粒度,兩段鎖協(xié)議,隱式和顯示鎖定。
一、封鎖粒度
MySQL 中提供了兩種封鎖粒度:
- 行級鎖
- 表級鎖。
應(yīng)該盡量只鎖定需要修改的那部分數(shù)據(jù),而不是所有的資源。鎖定的數(shù)據(jù)量越少,發(fā)生鎖爭用的可能就越小,系統(tǒng)的并發(fā)程度就越高。
但是加鎖需要消耗資源,鎖的各種操作(包括獲取鎖、釋放鎖、以及檢查鎖狀態(tài))都會增加系統(tǒng)開銷。因此封鎖粒度越小,系統(tǒng)開銷就越大。
在選擇封鎖粒度時,需要在鎖開銷和并發(fā)程度之間做一個權(quán)衡。
二、封鎖類型
1. 讀寫鎖
- 互斥鎖(Exclusive),簡寫為 X 鎖,又稱寫鎖。
- 共享鎖(Shared),簡寫為 S 鎖,又稱讀鎖。
有以下兩個規(guī)定:
- 一個事務(wù)對數(shù)據(jù)對象 A 加了 X 鎖,就可以對 A 進行讀取和更新。加鎖期間其它事務(wù)不能對 A 加任何鎖。
- 一個事務(wù)對數(shù)據(jù)對象 A 加了S 鎖,可以對 A 進行讀取操作,但是不能進行更新操作。加鎖期間其它事務(wù)能對 A 加 S 鎖,但是不能加 X 鎖。
2. 意向鎖
意向鎖的含義
意向鎖的含義是如果對一個結(jié)點加意向鎖,則說明該結(jié)點的下層結(jié)點正在被加鎖;對任一結(jié)點加鎖時,必須先對它的上層結(jié)點加意向鎖。
例如,對任一元組加鎖時,必須先對它所在的關(guān)系加意向鎖。
于是,事務(wù)T要對關(guān)系R1加 X鎖時,系統(tǒng)只要檢查根結(jié)點數(shù)據(jù)庫和關(guān)系R1是否己加了不相容的鎖,而不再需要搜索和檢查尺中的每一個元組是否加了X鎖。
意向鎖的作用
引進意向鎖是為了提高封鎖子系統(tǒng)的效率。該封鎖子系統(tǒng)支持多種封鎖粒度。原因是:在多粒度封鎖方法中一個數(shù)據(jù)對象可能以兩種方式加鎖 ― 顯式封鎖和隱式封鎖。因此系統(tǒng)在對某一數(shù)據(jù)對象加鎖時不僅要檢查該數(shù)據(jù)對象上有無(顯式和隱式)封鎖與之沖突,還要檢查其所有上級結(jié)點和所有下級結(jié)點,看申請的封鎖是否與這些結(jié)點上的(顯式和隱式)封鎖沖突,顯然,這樣的檢查方法效率很低。為此引進了意向鎖。意向鎖的含義是:對任一結(jié)點加鎖時,必須先對它的上層結(jié)點加意向鎖。例如事務(wù) T 要對某個元組加 X 鎖,則首先要對關(guān)系和數(shù)據(jù)庫加 ix 鎖。換言之,對關(guān)系和數(shù)據(jù)庫加 ix 鎖,表示它的后裔結(jié)點 ― 某個元組擬(意向)加 X 鎖。引進意向鎖后,系統(tǒng)對某一數(shù)據(jù)對象加鎖時不必逐個檢查與下一級結(jié)點的封鎖沖突了。例如,事務(wù) T 要對關(guān)系 R 加 X 鎖時,系統(tǒng)只要檢查根結(jié)點數(shù)據(jù)庫和 R 本身是否已加了不相容的鎖(如發(fā)現(xiàn)已經(jīng)加了 ix ,則與 X 沖突),而不再需要搜索和檢查 R 中的每一個元組是否加了 X 鎖或 S 鎖。
使用意向鎖(Intention Locks)可以更容易地支持多粒度封鎖。
在存在行級鎖和表級鎖的情況下,事務(wù) T 想要對表 A 加 X 鎖,就需要先檢測是否有其它事務(wù)對表 A 或者表 A 中的任意一行加了鎖,那么就需要對表 A 的每一行都檢測一次,這是非常耗時的。
意向鎖在原來的 X/S 鎖之上引入了 IX/IS,IX/IS 都是表鎖,用來表示一個事務(wù)想要在表中的某個數(shù)據(jù)行上加 X 鎖或 S 鎖。有以下兩個規(guī)定:
- 一個事務(wù)在獲得某個數(shù)據(jù)行對象的 S 鎖之前,必須先獲得表的 IS 鎖或者更強的鎖;
- 一個事務(wù)在獲得某個數(shù)據(jù)行對象的 X 鎖之前,必須先獲得表的 IX 鎖。
通過引入意向鎖,事務(wù) T 想要對表 A 加 X 鎖,只需要先檢測是否有其它事務(wù)對表 A 加了 X/IX/S/IS 鎖,如果加了就表示有其它事務(wù)正在使用這個表或者表中某一行的鎖,因此事務(wù) T 加 X 鎖失敗。
解釋如下:
- 任意 IS/IX 鎖之間都是兼容的,因為它們只表示想要對表加鎖,而不是真正加鎖;
- 這里兼容關(guān)系針對的是表級鎖,而表級的 IX 鎖和行級的 X 鎖兼容,兩個事務(wù)可以對兩個數(shù)據(jù)行加 X 鎖。(事務(wù) T1 想要對數(shù)據(jù)行 R1 加 X 鎖,事務(wù) T2 想要對同一個表的數(shù)據(jù)行 R2 加 X 鎖,兩個事務(wù)都需要對該表加 IX 鎖,但是 IX 鎖是兼容的,并且 IX 鎖與行級的 X 鎖也是兼容的,因此兩個事務(wù)都能加鎖成功,對同一個表中的兩個數(shù)據(jù)行做修改。)
三、封鎖協(xié)議
1. 三級封鎖協(xié)議
一級封鎖協(xié)議
事務(wù) T 要修改數(shù)據(jù) A 時必須加 X 鎖,直到 T 結(jié)束才釋放鎖。
可以解決丟失修改問題,因為不能同時有兩個事務(wù)對同一個數(shù)據(jù)進行修改,那么事務(wù)的修改就不會被覆蓋。
二級封鎖協(xié)議
在一級的基礎(chǔ)上,要求讀取數(shù)據(jù) A 時必須加 S 鎖,讀取完馬上釋放 S 鎖。
可以解決讀臟數(shù)據(jù)問題,因為如果一個事務(wù)在對數(shù)據(jù) A 進行修改,根據(jù) 1 級封鎖協(xié)議,會加 X 鎖,那么就不能再加 S 鎖了,也就是不會讀入數(shù)據(jù)。
三級封鎖協(xié)議
在二級的基礎(chǔ)上,要求讀取數(shù)據(jù) A 時必須加 S 鎖,直到事務(wù)結(jié)束了才能釋放 S 鎖。
可以解決不可重復(fù)讀的問題,因為讀 A 時,其它事務(wù)不能對 A 加 X 鎖,從而避免了在讀的期間數(shù)據(jù)發(fā)生改變。
2. 兩段鎖協(xié)議
加鎖和解鎖分為兩個階段進行。
調(diào)度兩段鎖協(xié)議:是指所有的事務(wù)必須分兩個階段對數(shù)據(jù)項加鎖和解鎖。即事務(wù)分兩個階段,第一個階段是獲得封鎖。事務(wù)可以獲得任何數(shù)據(jù)項上的任何類型的鎖,但是不能釋放;第二階段是釋放封鎖,事務(wù)可以釋放任何數(shù)據(jù)項上的任何類型的鎖,但不能申請。
第一階段是獲得封鎖的階段,稱為擴展階段:其實也就是該階段可以進入加鎖操作,在對任何數(shù)據(jù)進行讀操作之前要申請獲得S鎖,在進行寫操作之前要申請并獲得X鎖,加鎖不成功,則事務(wù)進入等待狀態(tài),直到加鎖成功才繼續(xù)執(zhí)行。就是加鎖后就不能解鎖了。
第二階段是釋放封鎖的階段,稱為收縮階段:當事務(wù)釋放一個封鎖后,事務(wù)進入封鎖階段,在該階段只能進行解鎖而不能再進行加鎖操作。
可串行化調(diào)度
多個事務(wù)的并發(fā)執(zhí)行是正確的,當且僅當其結(jié)果與按某一次序串行地執(zhí)行這些事務(wù)時結(jié)果相同。
可串行性(Serializability)是并發(fā)事務(wù)正確調(diào)度的準則。一個給定的并發(fā)調(diào)度,當且僅當它是可串行化的,才是正確的。
DBMS為了保證并發(fā)調(diào)度的正確性,普遍使用兩段鎖協(xié)議實現(xiàn)并發(fā)調(diào)度的可串行性,保證調(diào)度的正確性。
兩段鎖協(xié)議
- 在對任何數(shù)據(jù)進行讀、寫操作之前,事務(wù)首先要申請并獲得對該數(shù)據(jù)的封鎖;
- 在釋放一個封鎖之后,事務(wù)不再申請和獲得任何其他封鎖。
若并發(fā)的所有事務(wù)均遵守兩段鎖協(xié)議,則對這些事務(wù)的任何并發(fā)調(diào)度策略都是可串行化的。
兩段鎖協(xié)議和防止死鎖的一次封鎖法
- 一次封鎖法要求每個事務(wù)必須一次將所有要使用的數(shù)據(jù)全部加鎖,否則不能繼續(xù)執(zhí)行。一次封鎖法遵循兩段鎖協(xié)議;
- 兩段鎖協(xié)議并不要求事務(wù)必須一次將所有要使用的數(shù)據(jù)全部加鎖,因此遵循兩段鎖協(xié)議的事務(wù)可能發(fā)生死鎖。
事務(wù)遵循兩段鎖協(xié)議是保證可串行化調(diào)度的充分條件。例如以下操作滿足兩段鎖協(xié)議,它是可串行化調(diào)度。
lock-x(A)...lock-s(B)...lock-s(C)...unlock(A)...unlock(C)...unlock(B)
但不是必要條件,例如以下操作不滿足兩段鎖協(xié)議,但它還是可串行化調(diào)度。
lock-x(A)...unlock(A)...lock-s(B)...unlock(B)...lock-s(C)...unlock(C)
四、MySQL隱式與顯示鎖定
當多個客戶端并發(fā)訪問同一個數(shù)據(jù)的時候,為了保證數(shù)據(jù)的一致性,數(shù)據(jù)庫管理系統(tǒng)會自動的為該數(shù)據(jù)加鎖、解鎖,這種鎖被稱為隱式鎖。隱式鎖無需開發(fā)人員維護(包括鎖粒度、加鎖時機、解鎖時機等)
某些特殊情況下需要開發(fā)人員手動的進行加鎖、解鎖,這種鎖方式被稱為顯示鎖。對于顯示鎖而言,開發(fā)人員不僅要確定鎖的粒度,還需要確定加鎖的時機(何時加鎖)、解鎖的時機(何時解鎖)以及鎖的類型
MySQL 的 InnoDB 存儲引擎采用兩段鎖協(xié)議,會根據(jù)隔離級別在需要的時候自動加鎖,并且所有的鎖都是在同一時刻被釋放,這被稱為隱式鎖定。
InnoDB 也可以使用特定的語句進行顯示鎖定:
SELECT ... LOCK In SHARE MODE;
SELECT ... FOR UPDATE;