X | S | |
---|---|---|
X | 不兼容 | 不兼容 |
S | 不兼容 | 兼容 |
此外,InnoDB存儲引擎支持多粒度鎖定,這種鎖定允許事務(wù)在行級上鎖和表鎖上的鎖同時存在。為了支持在不同粒度上進(jìn)行加鎖操作,InnoDB存
儲引擎支持一種額外的鎖方式,稱之為意向鎖。意向鎖是將鎖定的對象分為多個層次,意向鎖意味著事務(wù)希望在更細(xì)粒度上進(jìn)行加鎖。 InnoDB存
儲引擎支持意向鎖設(shè)計比較簡練,其意向鎖即為表級別的鎖。設(shè)計目的主要是為了在一個事務(wù)中揭示下一行將被請求的鎖類型。其支持兩種意向鎖:
一致性的非鎖定讀(consistant nonlocking read)是指InnoDB存儲引擎通過多版本控制(multi versioning)的方法來讀取當(dāng)前執(zhí)行時間數(shù)據(jù)庫中行的
數(shù)據(jù)。如果讀取的行正在執(zhí)行Delete或Update操作,這時讀取操作不會因此去等待行上鎖的釋放。相反地,InnoDB存儲引擎會去讀取行的一個快照
版本。如下如所示。
上圖直觀地展現(xiàn)了InnoDB存儲引擎一致性的非鎖定讀。之所以稱為非鎖定讀,因為不需要等待訪問的行上X鎖的釋放。快照數(shù)據(jù)是指該行的之前版本
的數(shù)據(jù),該實現(xiàn)是通過undo段來完成。而undo用來在事務(wù)中回滾數(shù)據(jù),因此快照數(shù)據(jù)本身是沒有額外的開銷。此外,讀取快照數(shù)據(jù)是不需要上鎖的,
因為沒有事務(wù)需要對歷史的數(shù)據(jù)進(jìn)行修改操作。
通過上圖可以知道,快照數(shù)據(jù)其實就是當(dāng)前行數(shù)據(jù)之前的歷史版本,每行記錄可能有多個版本,一般稱這種技術(shù)為行多版本技術(shù)。由此帶來的并發(fā)控制,
稱之為多版本并發(fā)控制(Multi Version Concurrency Control, MVCC)。
在事務(wù)隔離級別READ COMMITTED和REPEATABLE READ下,InnoDB存儲引擎使用非鎖定的一致性讀。然而,對于快照數(shù)據(jù)的定義卻不相同。在READ
COMMITTED事務(wù)隔離級別下,對于快照數(shù)據(jù),非一致性讀總是讀取被鎖定行的最新一份快照數(shù)據(jù)。而在REPEATABLE READ事務(wù)隔離級別下,對于快照
數(shù)據(jù),非一致性讀總是讀取事務(wù)開始時的行數(shù)據(jù)版本。如下表所示示例:
時間 | 會話A | 會話B |
---|---|---|
1 | begin | |
2 | select * from t_user where id = 1; | |
3 | begin | |
4 | update t_user set id = 10 where id = 1; | |
5 | select * from t_user where id = 1; | |
6 | commit; | |
7 | select * from t_user where id = 1; | |
8 | commit; |
假設(shè)原本id = 1的記錄是存在的,大家可以按上表時間順序執(zhí)行對應(yīng)的會話,比較及驗證2者的不同。
在默認(rèn)配置下,在事務(wù)的隔離級別為REPEATABLE READ模式下,InnoDB存儲引擎的select操作使用一致性非鎖定讀。但是在某些情況下,用戶需要顯示地
對數(shù)據(jù)庫讀取操作進(jìn)行加鎖以保證數(shù)據(jù)邏輯的一致性。而這要求數(shù)據(jù)庫支持加鎖語句,即使時對于select的只讀操作。InnoDB存儲引擎對于select語句支持兩
種一致性的鎖定讀(locking read)操作:
select ··· for update對讀取的行記錄加一個X鎖,其他事務(wù)不能對已鎖定的行加上任何鎖。select ··· lock in share mode對讀取的行記錄加一個S鎖,其他事務(wù)可
以向被鎖定的行加S鎖,但是如果加X鎖,則會被阻塞。
對于一致性非鎖定讀,即使讀取的行已被執(zhí)行了select ··· for update,也是可以進(jìn)行讀取的。此外,select ··· for update或者select ··· lock in share mode必須在
一個事務(wù)中,當(dāng)事務(wù)提交了,鎖也就釋放了。因此在使用上述兩種select鎖定語句時,務(wù)必加上begin,start transaction或者set autocommit=0。
InnoDB存儲引擎有3種行鎖的算法,其分別是:
Record Lock總是會去鎖住主鍵索引記錄,如果InnoDB存儲引擎表在建立的時候沒有設(shè)置任何一個主鍵或唯一非空索引,那么這時InnoDB存儲引擎會使用隱式的
主鍵來進(jìn)行鎖定。
Next-Key Lock是結(jié)合了Gap Lock+Record Lock的一種鎖定算法,在Next-Key Lock算法下,InnoDB對于行的查詢都是采用這種鎖定算法。假如一個索引有10,11
,13和20這4個值,那么該索引可能被Next-Key Locking的區(qū)間為:
(-無窮,10] ,(10,11], (11,13], (13,20], (20,+無窮)
采用Next-Key Lock的鎖定技術(shù)稱為Next-Key Locking。其設(shè)計的目的是為了解決幻讀問題。而利用這種鎖定技術(shù),鎖定的不是單個值,而是一個范圍。 然而,
當(dāng)查詢的索引含有唯一屬性時,InnoDB存儲引擎會對Next-Key Lock進(jìn)行優(yōu)化將其降級為Record Lock,即僅鎖住索引本身,而不是范圍。下面演示一個例子。
mysql> create table t (a int primary key); Query OK, 0 rows affected (0.01 sec) mysql> insert into t select 1; Query OK, 1 row affected (0.00 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql> insert into t select 2; Query OK, 1 row affected (0.00 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql> insert into t select 5; Query OK, 1 row affected (0.01 sec) Records: 1 Duplicates: 0 Warnings: 0
接著按下表時間順序執(zhí)行操作。
時間 | 會話A | 會話B |
---|---|---|
1 | begin; | |
2 | select * from t where a = 5 for update; | |
3 | begin; | |
4 | insert into t select 4; | |
5 | commit; #成功,不需要等待 | |
6 | commit; |
表t共有1,2,5三個值。在上面的例子中,在會話A中首先對a=5進(jìn)行X鎖定。而由于a是主鍵且唯一,因此鎖定的僅是5這個值,而不是(2,5)這個范圍,這樣在會話
B中插入值4而不會阻塞,可以立即插入并返回。即鎖定由Next-Key Lock算法降級為了Record Lock,從而提高應(yīng)用的并發(fā)性。
如上,Next-Key Lock降級為Record Lock僅在查詢的列是唯一索引的情況下。若是輔助索引,則情況會完全不同。同樣,首先創(chuàng)建測試表z進(jìn)行測試:
mysql> create table z (a int ,b int ,primary key(a), key(b)); mysql> insert into z select 1,1; mysql> insert into z select 3,1; mysql> insert into z select 5,3; mysql> insert into z select 7,6; mysql> insert into z select 10,8;
表z的列b是輔助索引,若在會話A中執(zhí)行下面的SQL語句:
mysql> select * from z where b = 3 for update;
很明顯,這時SQL語句通過索引列b進(jìn)行查詢,因此其使用傳統(tǒng)的Next-Key Locking技術(shù)加鎖,并且由于有兩個索引,其需要分別進(jìn)行鎖定。對于聚集索引,其僅對列
a等于5的索引加上Record Lock。而對于輔助索引,其加上的是Next-Key Lock,鎖定的范圍是(1,3),特別需要注意的是,InnoDB存儲引擎還會對輔助索引下一個
鍵值加上gap lock,即還有一個輔助索引范圍為(3,6)的鎖。因此,若在新會話B中運行下面的SQL語句,都會被阻塞:
mysql> select * from z where a = 5 lock in share mode; mysql> insert into z select 4,2; mysql> insert into z select 6,5;
第一個SQL語句不能執(zhí)行,因為在會話A中執(zhí)行的SQL語句已經(jīng)對聚集索引中列a=5的值加上X鎖,因此執(zhí)行會被阻塞。第二個SQL語句,主鍵插入4,沒有問題,但是插入
的輔助索引值2在鎖定的范圍(1,3)中,因此執(zhí)行同樣會被阻塞。第三個SQL語句,插入的主鍵6沒有被鎖定,5也不在范圍(1,3)之間。但插入的值5在另一個鎖定的
范圍(3,6)中,故同樣需要等待。而下面的SQL語句,不會被阻塞,可以立即執(zhí)行:
mysql> insert into z select 8,6; mysql> insert into z select 2,0; mysql> insert into z select 6,7;
從上面的例子可以看到,Gap Lock的作用是為了阻止多個事務(wù)將記錄插入到同一個范圍內(nèi),而這會導(dǎo)致幻讀問題的產(chǎn)生。假如在上面的例子中,會話A中用戶已經(jīng)鎖定了
b=3的記錄。若此時沒有Gap Lock鎖定(3,6),那么用戶可以插入索引b列為3的記錄,這會導(dǎo)致會話A中的用戶再次執(zhí)行同樣查詢時會返回不同的記錄,即幻讀。
這里主要探究的是InnoDB存儲引擎鎖表的機(jī)制,至少自己明白了Mysql的行鎖機(jī)制,不知道讀者是否有疑問,歡迎留言。下次會記錄關(guān)于Mysql事務(wù)特性及其內(nèi)部的實現(xiàn)機(jī)制,
包括mysql的內(nèi)部架構(gòu),InnoDB buffer Pool,redo log, undo log等具體的詳解,目前只是對知識過了一遍,但還未總結(jié)。
到此這篇關(guān)于Mysql技術(shù)內(nèi)幕之InnoDB鎖的文章就介紹到這了,更多相關(guān)Mysql InnoDB鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
標(biāo)簽:黔東 珠海 拉薩 移動 徐州 沈陽 鹽城 沈陽
巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《Mysql技術(shù)內(nèi)幕之InnoDB鎖的深入講解》,本文關(guān)鍵詞 Mysql,技術(shù),內(nèi)幕,之,InnoDB,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。