InnoDB中鎖的算法(1)

Ⅰ、InnoDB鎖算法的介紹

首先明確一點,鎖鎖住的是什麼?鎖鎖住的是索引算法

  • Record Lock
    單個行記錄上的鎖
  • Gap Lock
    鎖定一個範圍,但不包含記錄自己
  • Next-key Lock
    Gap Lock + Record Lock 鎖定一個範圍,而且鎖定記錄自己

Ⅱ、模擬加鎖場景

(root@localhost) [test]> desc l;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| a     | int(11) | NO   | PRI | NULL    |       |
| b     | int(11) | YES  | MUL | NULL    |       |
| c     | int(11) | YES  | UNI | NULL    |       |
| d     | int(11) | YES  |     | NULL    |       |
+-------+---------+------+-----+---------+-------+
4 rows in set (0.00 sec)

(root@localhost) [test]> select * from l;
+---+------+------+------+
| a | b    | c    | d    |
+---+------+------+------+
| 2 |    4 |    6 |    8 |
| 4 |    6 |    8 |   10 |
| 6 |    8 |   10 |   12 |
| 8 |   10 |   12 |   14 |
+---+------+------+------+
4 rows in set (0.02 sec)

(root@localhost) [test]> begin;
Query OK, 0 rows affected (0.00 sec)

(root@localhost) [test]> select * from l where a = 2 for update;
+---+------+------+------+
| a | b    | c    | d    |
+---+------+------+------+
| 2 |    4 |    6 |    8 |
+---+------+------+------+
1 row in set (0.03 sec)

對主鍵爲2的這條記錄加鎖,這裏能夠表示三個意思
①record lock:對2加X鎖

②gap lock:對(負無窮,2)加X鎖
    thd1:hold 2 x gap
    thd2:hold 2 x record
    上面兩個是兼容的,也就是說,thd2直接操做2這條記錄是能夠操做的,不須要等待
    thd3:insert 1,這個線程就要wait,由於1在這個範圍內

③next-key lock 鎖住(負無窮,2] 
  
oralce中只有record lock,沒有別的意思

通常來講,此處咱們根據不一樣事務隔離級別來分析這個加鎖狀況以下:併發

  • rc
    全部某條記錄的加鎖都是record鎖,全部insert不用等待,併發度更好
    --->lock_mode X locks rec but not gap
  • rr
    全部對某條記錄加鎖都用的next-key locking,insert 並行性能或許有點差
    --->lock_mode X

特殊狀況:
會把加鎖模式優化爲record lock,前提是鎖住的那個index是unique的,而且只返回(鎖住)一條記錄性能

(a,b)複合索引,查a=? 用的仍是next-key locking,查a=?,b=?就會用record lock優化

Ⅲ、正兒八經的分析幾個場景看看

3.1 對主鍵加鎖

(root@localhost) [test]> show variables like 'tx_isolation';                                    
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| tx_isolation  | REPEATABLE-READ |
+---------------+-----------------+
1 row in set (0.01 sec)

(root@localhost) [test]> begin;
Query OK, 0 rows affected (0.00 sec)

(root@localhost) [test]> select * from l where a <=2 for update;
+---+------+------+------+
| a | b    | c    | d    |
+---+------+------+------+
| 2 |    4 |    6 |    8 |
+---+------+------+------+
1 row in set (0.01 sec)

(root@localhost) [test]> show engine innodb status\G
...
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 31220336, ACTIVE 16 sec
2 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 416, OS thread handle 139830453040896, query id 5627 localhost root starting
show engine innodb status
TABLE LOCK table `test`.`l` trx id 31220336 lock mode IX
RECORD LOCKS space id 1358 page no 3 n bits 72 index PRIMARY of table `test`.`l` trx id 31220336 lock_mode X
Record lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
 0: len 4; hex 80000002; asc     ;;
 1: len 6; hex 000001c1b939; asc      9;;
 2: len 7; hex e0000001a80110; asc        ;;
 3: len 4; hex 80000004; asc     ;;
 4: len 4; hex 80000006; asc     ;;
 5: len 4; hex 80000008; asc     ;;

Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
 0: len 4; hex 80000004; asc     ;;
 1: len 6; hex 000001c1b93a; asc      :;;
 2: len 7; hex e1000001a90110; asc        ;;
 3: len 4; hex 80000006; asc     ;;
 4: len 4; hex 80000008; asc     ;;
 5: len 4; hex 8000000a; asc     ;;
...

按道理咱們鎖住的應該是(負無窮,2],但實際上鎖住的範圍已經到了4這條記錄,此時插入3是插不進去的,爲何?spa

爲了保證解決幻讀,要把2到它後面這條記錄4這段範圍鎖住,這時候若是新插入一個2,在原來的2後面是插不進來的,若是4不鎖住,新開一個線程能夠刪除4,又能夠新插入一個4線程

rc的話就是隻鎖住記錄自己,以下:code

(root@localhost) [(none)]> show variables like 'tx_isolation';
+---------------+----------------+
| Variable_name | Value          |
+---------------+----------------+
| tx_isolation  | READ-COMMITTED |
+---------------+----------------+
1 row in set (0.00 sec)

(root@localhost) [(none)]> begin;
Query OK, 0 rows affected (0.00 sec)

(root@localhost) [test]> select * from l where a <=2 for update;
+---+------+------+------+
| a | b    | c    | d    |
+---+------+------+------+
| 2 |    4 |    6 |    8 |
+---+------+------+------+
1 row in set (0.00 sec)

(root@localhost) [test]> show engine innodb status\G
...
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 31220337, ACTIVE 6 sec
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 443, OS thread handle 139830452774656, query id 5649 localhost root starting
show engine innodb status
TABLE LOCK table `test`.`l` trx id 31220337 lock mode IX
RECORD LOCKS space id 1358 page no 3 n bits 72 index PRIMARY of table `test`.`l` trx id 31220337 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
 0: len 4; hex 80000002; asc     ;;
 1: len 6; hex 000001c1b939; asc      9;;
 2: len 7; hex e0000001a80110; asc        ;;
 3: len 4; hex 80000004; asc     ;;
 4: len 4; hex 80000006; asc     ;;
 5: len 4; hex 80000008; asc     ;;
...

惟一索引和主鍵狀況同樣orm

3.2 對二級索引加鎖

先看rc事務隔離級別索引

(root@localhost) [test]> begin;
Query OK, 0 rows affected (0.00 sec)

(root@localhost) [test]> select * from l where b = 6 for update;
+---+------+------+------+
| a | b    | c    | d    |
+---+------+------+------+
| 4 |    6 |    8 |   10 |
+---+------+------+------+
1 row in set (0.02 sec

(root@localhost) [test]> show engine innodb status\G
...
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 31220338, ACTIVE 35 sec
3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 443, OS thread handle 139830452774656, query id 5653 localhost root starting
show engine innodb status
TABLE LOCK table `test`.`l` trx id 31220338 lock mode IX
RECORD LOCKS space id 1358 page no 5 n bits 72 index b of table `test`.`l` trx id 31220338 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 80000006; asc     ;;
 1: len 4; hex 80000004; asc     ;;

RECORD LOCKS space id 1358 page no 3 n bits 72 index PRIMARY of table `test`.`l` trx id 31220338 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
 0: len 4; hex 80000004; asc     ;;
 1: len 6; hex 000001c1b93a; asc      :;;
 2: len 7; hex e1000001a90110; asc        ;;
 3: len 4; hex 80000006; asc     ;;
 4: len 4; hex 80000008; asc     ;;
 5: len 4; hex 8000000a; asc     ;;
...

先對二級索引b加record鎖:lock_mode X locks rec but not gap鎖住了(6,4),6是二級索引,4是主鍵值事務

再對彙集索引加鎖也是record locks,鎖彙集索引index primary,鎖住了a=4

再分析rr隔離級別下的狀況,以下:

(root@localhost) [test]> show variables like 'tx_isolation';
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| tx_isolation  | REPEATABLE-READ |
+---------------+-----------------+
1 row in set (0.00 sec)

(root@localhost) [test]> begin;
Query OK, 0 rows affected (0.00 sec)

(root@localhost) [test]> select * from l where b = 6 for update;
+---+------+------+------+
| a | b    | c    | d    |
+---+------+------+------+
| 4 |    6 |    8 |   10 |
+---+------+------+------+
1 row in set (0.01 sec)

(root@localhost) [test]> show engine innodb status\G
...
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 31220340, ACTIVE 5 sec
4 lock struct(s), heap size 1136, 3 row lock(s)
MySQL thread id 444, OS thread handle 139830446065408, query id 5673 localhost root starting
show engine innodb status
TABLE LOCK table `test`.`l` trx id 31220340 lock mode IX
RECORD LOCKS space id 1358 page no 5 n bits 72 index b of table `test`.`l` trx id 31220340 lock_mode X
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 80000006; asc     ;;
 1: len 4; hex 80000004; asc     ;;

RECORD LOCKS space id 1358 page no 3 n bits 72 index PRIMARY of table `test`.`l` trx id 31220340 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
 0: len 4; hex 80000004; asc     ;;
 1: len 6; hex 000001c1b93a; asc      :;;
 2: len 7; hex e1000001a90110; asc        ;;
 3: len 4; hex 80000006; asc     ;;
 4: len 4; hex 80000008; asc     ;;
 5: len 4; hex 8000000a; asc     ;;

RECORD LOCKS space id 1358 page no 5 n bits 72 index b of table `test`.`l` trx id 31220340 lock_mode X locks gap before rec
Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 80000008; asc     ;;
 1: len 4; hex 80000006; asc     ;;
...

這個就稍微有點複雜了,依稀能夠看到是加了三個鎖,咱們挨個分析一波

  • 第一個鎖鎖住索引b(4,6],next-key lock鎖 lock_mode X
  • 第二個鎖是對主鍵a=4這條惟一記錄的主鍵上加一個記錄鎖(由於惟一),lock_mode X locks rec but not gap
  • 第三個鎖是gap before rec 鎖住了b(6,8),也就是對8加了gap

爲何要鎖住(6,8)?

假設不鎖住這塊,一個線程插入了(3,6),只鎖住(4,6]那就能夠插入了,那原來的線程第一次返回的只有一條b=6的記錄,那第二次執行就出現了兩條b=6,就幻讀了

tips: 新插入的6是在(6,8)這個範圍裏的,新插入的相同的記錄,都在已存在的記錄後面 4 6 6(新插) 8

相關文章
相關標籤/搜索