【mysql】- 鎖篇(上)
阿新 • • 發佈:2020-07-14
回顧
問題
- 事務併發執行時可能帶來各種問題,併發事務訪問相同記錄的情況大致可以劃分為3種
讀-讀
情況:即併發事務相繼讀取相同的記錄- 讀取操作本身不會對記錄有什麼影響,並不會引起什麼問題,所以允許這種情況的發
寫-寫
情況:即併發事務相繼對相同的記錄做出改動- 任何一種隔離級別都不允許這種問題的發生。所以在多個未提交事務相繼對一條記錄做改動時,需要讓它們排隊執行,這個排隊的過程其實是通過
鎖
來實現的。這個所謂的鎖
其實是一個記憶體中的結構,在事務執行前本來是沒有鎖的,也就是說一開始是沒有鎖結構
和記錄進行關聯的
- 任何一種隔離級別都不允許這種問題的發生。所以在多個未提交事務相繼對一條記錄做改動時,需要讓它們排隊執行,這個排隊的過程其實是通過
讀-寫
或寫-讀
情況:也就是一個事務進行讀取操作,另一個進行改動操作- 這種情況下可能發生
髒讀
不可重複讀
、幻讀
的問題
- 這種情況下可能發生
解決上述問題方案
- 讀操作利多版本併發控制( MVCC ),寫操作進行
加鎖
- 讀、寫操作都採用
加鎖
的方
一致性讀(Consistent Reads)
- 事務利用
MVCC
進行的讀取操作稱之為一致性讀
,或者一致性性鎖讀
,有的地也稱之為快照讀
。所有普通的SELECT
語句( plain SELECT )在READ COMMITTED
、REPEATABLE READ
隔離級別下都算是一致性讀
,一致性讀
並不會對錶中的任何記錄做加鎖
操作,其他事務可以自由的對錶中的記錄做改動
鎖定讀(Locking Reads)
併發事務的
讀-讀
情況並不會引起什麼問題,不過對於寫-寫
、讀-寫
或寫-讀
這些情況可能會引起一些問題,需要使用MVCC
或者加鎖
的方式來解決它們。在使用加鎖
的方式解決問題時,由於既要允許讀-讀
情況不受影響,又要使寫-寫
、讀-寫
或寫-讀
情況中的操作相互阻塞
-
共享鎖和獨佔鎖
- 共享鎖 ,英文名:
Shared Locks
,簡稱S鎖
。在事務要讀取一條記錄時,需要先獲取該記錄的S鎖
- 獨佔鎖 ,也常稱
排他鎖
,英文名:Exclusive Locks
,簡稱X鎖
。在事務要改動一條記錄時,需要先獲取該記錄的X鎖
- 上述兩種鎖的場景
- 假如事務
T1
首先獲取了一條記錄的S鎖
之後,事務T2
接著也要訪問這條記錄:- 如果事務
T2
想要再獲取一個記錄的S鎖
,那麼事務T2
也會獲得該鎖,也就意味著事務T1
T2
在該記錄上同時持有S鎖
- 如果事務
T2
想要再獲取一個記錄的X鎖
,那麼此操作會被阻塞,直到事務T1
提交之後將S鎖
釋放掉
- 如果事務
- 如果事務
T1
首先獲取了一條記錄的X鎖
之後,那麼不管事務T2
接著想獲取該記錄的S鎖
還是X鎖
都會被阻塞,直到事務T1
提交 - 故我們說
S鎖
和S鎖
是相容的,S鎖
和X鎖
是不相容的,X鎖
和X鎖
也是不相容的
- 假如事務
- 共享鎖 ,英文名:
-
鎖定讀的語句
- 在採用
加鎖
方式解決髒讀
、不可重複讀
、幻讀
這些問題時,讀取一條記錄時需要獲取該下該記錄的S鎖
,其實這是不嚴謹的,有時候想在讀取記錄時就獲取記錄的X鎖
,來禁用別的事務讀寫該記錄- 對讀取的記錄加
S鎖
:- 也就是在普通的
SELECT
語句後邊加,如果當前事務執行了該語句,那麼它會為讀取到的記錄加S鎖
,這樣允許別的事務繼續獲取這些記錄的S鎖
(比方說別的事務也使用語句來讀取這些記錄),但是不能獲取這些記錄的X鎖
(比方說使SELECT ... FOR UPDATE
語句來讀取這些記錄,或者直接修改這些記錄)。如果別的事務想要獲取這些記錄的X鎖
,那麼它們會阻塞,直到當前事務提交之後將這些記錄上的S鎖
釋放掉
- 也就是在普通的
- 對讀取的記錄加
X鎖
:SELECT ... FOR UPDATE;
也就是在普通的SELECT
語句後邊加FOR UPDATE
,如果當前事務執行了該語句,那麼它會為讀取到的記錄加X鎖
,這樣既不允許別的事務獲取這些記錄的S鎖
(比方說別的事務使用語句來讀取這些記錄),也不允許獲取這些記錄的X鎖
(比方說使用SELECT ... FOR UPDATE
語句來讀取這些記錄,或者直接修改這些記錄)。如果別的事務想要獲取這些記錄的S鎖
或者X鎖
,那麼它們會阻塞,直到當前事務提交之後將這些記錄上的X鎖
釋放掉
- 對讀取的記錄加
- 在採用
-
寫操作
- 平常所用到的
寫操作
是DELETE
、UPDATE
、INSERT
這三種:- DELETE:對一條記錄做
DELETE
操作的過程其實是先在B+
樹中定位到這條記錄的位置,然後獲取一下這條記錄的X鎖
,然後再執行delete mark
操作。我們也可以把這個定位待刪除記錄在B+
樹中位置的過程看成是一個獲取X鎖
的鎖定讀 - UPDATE:
- 在對一條記錄做
UPDATE
操作時分為三種情況:- 如果未修改該記錄的鍵值並且被更新的列佔用的儲存空間在修改前後未發生變化,則先在
B+
樹中定位到這條記錄的位置,然後再獲取一下記錄的X鎖
,最後在原記錄的位置進行修改操作。其實我們也可以把這個定位待修改記錄在B+
樹中位置的過程看成是一個獲取X鎖
的鎖定讀
- 如果未修改該記錄的鍵值並且至少有一個被更新的列佔用的儲存空間在修改前後發生變化,則先在
B+
樹中定位到這條記錄的位置,然後獲取一下記錄的X鎖
,將該記錄徹底刪除掉(就是把記錄徹底移入垃圾連結串列),最後再插入一條新記錄。這個定位待修改記錄在B+
樹中位置的過程看成是一個獲取X鎖
的鎖定讀
,新插入的記錄由INSERT
操作提供的隱式鎖
進行保護 - 如果修改了該記錄的鍵值,則相當於在原記錄上做
DELETE
操作之後再來一次INSERT
操作,加鎖操作就需要按照DELETE
和INSERT
的規則進行了
- 如果未修改該記錄的鍵值並且被更新的列佔用的儲存空間在修改前後未發生變化,則先在
- 在對一條記錄做
- NSERT:一般情況下,新插入一條記錄的操作並不加鎖,通過一種稱之為
隱式鎖
來保護這條新插入的記錄在本事務提交前不被別的事務訪問
- DELETE:對一條記錄做
- 平常所用到的
前邊提到的
鎖
都是針對記錄的,也可以被稱之為行級鎖
或者行鎖
,對一條記錄加鎖影響的也只是這條記錄而已,我們就說這個鎖的粒度比較細;其實一個事務也可以在表級別進行加鎖,自然就被稱之為表級鎖
或者表鎖
,對一個表加鎖影響整個表中的記錄,我們就說這個鎖的粒度比較粗。給表加的鎖也可以分為共享鎖
(S鎖
)和獨佔鎖
(X鎖
)
- 多粒度鎖
- 給表加
S鎖
:- 如果一個事務給表加了
S鎖
,那麼:- 別的事務可以繼續獲得該表的
S鎖
- 別的事務可以繼續獲得該表中的某些記錄的
S鎖
- 別的事務不可以繼續獲得該表的
X鎖
- 別的事務不可以繼續獲得該表中的某些記錄的
X鎖
- 別的事務可以繼續獲得該表的
- 如果一個事務給表加了
- 給表加
X鎖
:- 如果一個事務給表加了
X鎖
(意味著該事務要獨佔這個表),那麼:- 別的事務不可以繼續獲得該表的
S鎖
- 別的事務不可以繼續獲得該表中的某些記錄的
S鎖
- 別的事務不可以繼續獲得該表的
X鎖
- 別的事務不可以繼續獲得該表中的某些記錄的
X鎖
- 別的事務不可以繼續獲得該表的
- 如果一個事務給表加了
- 給表加