
參考http://blog.csdn.net/zbszhangbosen/article/details/7434637#reply
這里補(bǔ)充一些:
(1)InnoDB默認(rèn)加鎖方式是next-key locking
(2)在聚集索引中,如果主鍵有唯一性約束(unique,auto increment),next-key locking 會(huì)自動(dòng)降級(jí)為record locking。
(3)由于事務(wù)的隔離性和一致性要求,會(huì)對(duì)所有掃描到的record加鎖。比如:update ... where/delete .. where/select ...from...lock in share mode/ select .. from .. for update這都是next-key lock。
(4)注意優(yōu)化器的選擇。包括聚集索引和輔助索引,有時(shí)會(huì)用全表掃描替代索引掃描,這時(shí)整張表(聚集索引表)都會(huì)被加鎖。
record lock:記錄鎖,也就是僅僅鎖著單獨(dú)的一行
gap lock:區(qū)間鎖,僅僅鎖住一個(gè)區(qū)間(注意這里的區(qū)間都是開區(qū)間,也就是不包括邊界值,至于為什么這么定義?innodb官方定義的)
next-key lock:record lock+gap lock,所以next-key lock也就半開半閉區(qū)間,且是下界開,上界閉。(為什么這么定義?innodb官方定義的)
下面來舉個(gè)手冊(cè)上的例子看什么是next-key lock。假如一個(gè)索引的行有10,11,13,20
那么可能的next-key lock的包括:
(無窮小, 10]
(10,11]
(11,13]
(13,20]
(20, 無窮大) (這里無窮大為什么不是閉合?你數(shù)學(xué)不到家~~)
好了現(xiàn)在通過舉例子說明:
表test
mysql> show create table test;
+-------+--------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+--------------------------------------------------------------------------------------------------------+
| test | CREATE TABLE `test` (
`a` int(11) NOT NULL,
PRIMARY KEY (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-------+--------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> select * from test;
+----+
| a |
+----+
| 11 |
| 12 |
| 13 |
| 14 |
+----+
4 rows in set (0.00 sec)
開始實(shí)驗(yàn):
(一)
session 1:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> delete from test where a=11;
Query OK, 1 row affected (0.00 sec)
session 2:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test values(10);
Query OK, 1 row affected (0.00 sec)
mysql> insert into test values(15);
Query OK, 1 row affected (0.00 sec)
mysql> insert into test values(9);
Query OK, 1 row affected (0.00 sec)
mysql> insert into test values(16);
Query OK, 1 row affected (0.01 sec)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
ok,上面的情況是預(yù)期的,因?yàn)閍上有索引,那么當(dāng)然就只要鎖定一行,所以其他行的插入不會(huì)被阻塞。
那么接下來的情況就有意思了
(二)
session 1(跟上一個(gè)session 1相同):
delete from test where a=22;
Query OK, 0 rows affected (0.01 sec)
session 2:
mysql> insert into test values (201);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (20);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (19);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (18);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (16);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (9);
Query OK, 1 row affected (0.00 sec)
從上面的結(jié)果來看,在a=11后面所有的行,也就是區(qū)間(11,無窮大)都被鎖定了。先不解釋原因,再來看一種情況:
(三)
session 1:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test;
+----+
| a |
+----+
| 7 |
| 9 |
| 10 |
| 12 |
| 13 |
| 14 |
| 15 |
| 22 |
| 23 |
| 24 |
| 25 |
+----+
11 rows in set (0.00 sec)
mysql> delete from test where a=21;
Query OK, 0 rows affected (0.00 sec)
session 2:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test values (20);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (26);
Query OK, 1 row affected (0.00 sec)
mysql> insert into test values (21);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (16);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (6);
Query OK, 1 row affected (0.01 sec)
從這里可以看出,現(xiàn)在被鎖住的區(qū)間就只有[16,21)了。
有了前面對(duì)三種類型的加鎖解釋,現(xiàn)在可以來解釋為什么會(huì)這樣了,在innodb表中 delete from where ..針對(duì)掃描到的索引記錄加next-key鎖(具體的什么語句加什么鎖可以查看手冊(cè),另外需要說明一下,行鎖加鎖對(duì)象永遠(yuǎn)是索引記錄,因?yàn)閕nnodb中表即索引) 。
在(一)中,實(shí)際上加的next-key lock就是(11,11] 因此也只有a=11這一條記錄被鎖住,其他所有插入都沒有關(guān)系。
在(二)中,因?yàn)閍=22這條記錄不存在,而且22比表里所有的記錄值都大,所以在innodb看來鎖住的區(qū)間就是(14, 無窮大)。所以在插入14以后的值都提示被鎖住,而14之前的則可以。
在(三)種,a=21也是不存在,但是在表里面21前后都有記錄,因此這里next-key lock的區(qū)間也就是(15,21],因此不在這個(gè)區(qū)間內(nèi)的都可以插入。
那么為什么next-key lock都是下界開區(qū)間,上界閉區(qū)間呢?這個(gè)倒不重要,管它呢,但是有一點(diǎn)我個(gè)人卻覺得比較怪,比如說
delete test where a > 11 #------- 1
它的next-key lock是(11, 無窮大)
delete test where a < 11 #------- 2
它的next-key lock是(無窮小, 10]
這樣給人的感覺就很怪,因?yàn)樵谑謨?cè)上對(duì)next-key lock的定義:
Next-key lock: This is a combination of a record lock on the index record and a gap lock on the gapbefore the index record.
而在1那種情況下,如果按照手冊(cè)上的解釋,記錄鎖和它之前的gap那么就會(huì)有些牽強(qiáng)。[今天再次看了一遍官方手冊(cè),是之前自己的理解不到位,這個(gè)before是對(duì)的,因?yàn)閕nnodb在加鎖時(shí)是所有掃描過程中遇到的記錄都會(huì)被加鎖,那么對(duì)于1那種情況,實(shí)際上是從12開始掃描,但是因?yàn)橐WCa>11的都被delete掉,因此得一直掃描下去那自然最大值就是無窮大,因?yàn)檫@個(gè)next-key lock就是無窮大這條記錄(這是假設(shè)的一條記錄,表示一個(gè)邊界)加上它之前的gap lock (11, 無窮大),所以在任何時(shí)候next-lock都是record lock加上這個(gè)record之前的一個(gè)gap lock]
但是只要我們自己能理解就行了:記錄鎖---鎖單條記錄;區(qū)間鎖---鎖一個(gè)開區(qū)間;next-key 鎖---前面兩者的結(jié)合,而不要管什么before。
另外next-key lock雖然在很多時(shí)候是鎖一個(gè)區(qū)間,但要明白一個(gè)區(qū)間也可能只有一個(gè)元素,因此在稱delete from tb where key=x 這種情況下加next-key鎖也是完全正確的。
另外還提兩點(diǎn):
1.如果我們的SQL語句里面沒有利用到索引,那么加鎖對(duì)象將是所有行(但不是加表鎖),所以建索引是很重要的
2.next-key lock是為防止幻讀的發(fā)生,而只有repeatable-read以及以上隔離級(jí)別才能防止幻讀,所以在read-committed隔離級(jí)別下面沒有next-key lock這一說法。
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com