innodb next-key lock解析

參考http://blog.csdn.net/zbszhangbosen/article/details/7434637#replymysql

這裏補充一些:sql

(1)InnoDB默認加鎖方式是next-key lockingsession

(2)在彙集索引中,假設主鍵有惟一性約束(unique,auto increment),next-key locking 會本身主動降級爲record locking。post

(3)由於事務的隔離性和一致性要求,會對所有掃描到的record加鎖。優化

比方:update ... where/delete .. where/select ...from...lock in share mode/ select .. from .. for update這都是next-key lock。.net

(4)注意優化器的選擇。rest

包含彙集索引和輔助索引。有時會用全表掃描替代索引掃描。這時整張表(彙集索引表)都會被加鎖。對象

record lock:記錄鎖,也就是隻鎖着單獨的一行blog

gap lock:區間鎖。只鎖住一個區間(注意這裏的區間都是開區間。也就是不包含邊界值,至於爲何這麼定義?innodb官方定義的)
next-key lock:record lock+gap lock,因此next-key lock也就半開半閉區間,且是下界開,上界閉。(爲何這麼定義?innodb官方定義的)
如下來舉個手冊上的樣例看什麼是next-key lock。

假如一個索引的行有10,11,13,20
那麼可能的next-key lock的包含:
(無窮小, 10]
(10,11]
(11,13]
(13,20]
(20, 無窮大) (這裏無窮大爲何不是閉合?你數學不到家~~)
好了現在經過舉樣例說明:


表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)
開始實驗:
(一)
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,上面的狀況是預期的。因爲a上有索引。那麼固然就僅僅要鎖定一行,因此其它行的插入不會被堵塞。索引


那麼接下來的狀況就有意思了
(二)
session 1(跟上一個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)


從上面的結果來看,在a=11後面所有的行,也就是區間(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)


從這裏可以看出。現在被鎖住的區間就僅僅有[16,21)了。
有了前面對三種類型的加鎖解釋,現在可以來解釋爲何會這樣了,在innodb表中 delete from where ..針對掃描到的索引記錄加next-key鎖(詳細的什麼語句加什麼鎖可以查看手冊,另外需要說明一下。行鎖加鎖對象永遠是索引記錄,因爲innodb中表即索引) 。
在(一)中。實際上加的next-key lock就是(11,11] 所以也僅僅有a=11這一條記錄被鎖住。其它所有插入都沒有關係。
在(二)中,因爲a=22這條記錄不存在,而且22比表裏所有的記錄值都大,因此在innodb看來鎖住的區間就是(14, 無窮大)。

因此在插入14之後的值都提示被鎖住,而14以前的則可以。


在(三)種。a=21也是不存在,但是在表裏面21先後都有記錄。所以這裏next-key lock的區間也就是(15,21],所以不在這個區間內的都可以插入。
那麼爲何next-key lock都是下界開區間。上界閉區間呢?這個倒不重要,管它呢,但是有一點我我的卻認爲比較怪,比方說
delete test where a > 11           #------- 1
它的next-key lock是(11, 無窮大) 
delete test where a < 11           #------- 2
它的next-key lock是(無窮小, 10]
這樣給人的感受就很是怪。因爲在手冊上對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那種狀況下,若是依照手冊上的解釋,記錄鎖和它以前的gap那麼就會有些牽強。

[今天再次看了一遍官方手冊,是以前本身的理解不到位,這個before是對的,因爲innodb在加鎖時是所有掃描過程當中遇到的記錄都會被加鎖,那麼對於1那種狀況,其實是從12開始掃描,但是因爲要保證a>11的都被delete掉。所以得一直掃描下去那天然最大值就是無窮大,因爲這個next-key lock就是無窮大這條記錄(這是若是的一條記錄,表示一個邊界)加上它以前的gap lock (11, 無窮大),因此在不論何時next-lock都是record lock加上這個record以前的一個gap lock]
但是僅僅要咱們本身能理解便可了:記錄鎖---鎖單條記錄。區間鎖---鎖一個開區間。next-key 鎖---前面二者的結合,而不要管什麼before。
另外next-key lock儘管在很是多時候是鎖一個區間,但要明確一個區間也可能僅僅有一個元素。所以在稱delete from tb where key=x 這樣的狀況下加next-key鎖也是全然正確的。

另外還提兩點: 1.假設咱們的SQL語句裏面沒有利用到索引。那麼加鎖對象將是所有行(但不是加表鎖)。因此建索引是很是重要的 2.next-key lock是爲防止幻讀的發生,而僅僅有repeatable-read以及以上隔離級別才幹防止幻讀。因此在read-committed隔離級別如下沒有next-key lock這一說法。

相關文章
相關標籤/搜索