InnoDB中鎖的算法(2)

Ⅰ、上節回顧

session1:session

(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.00 sec)

(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 b = 6 for update;
+---+------+------+------+
| a | b    | c    | d    |
+---+------+------+------+
| 4 |    6 |    8 |   10 |
+---+------+------+------+
1 row in set (0.09 sec)

pk  2   4   6   8
key 4   6   8   10
二級索引鎖住的是(4,6]&&(6,8)
主鍵鎖住的是4

session2:併發

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

(root@localhost) [test]> insert into l values (3,4,14,20);

hang~~~

session1:性能

(root@localhost) [(none)]> show engine innodb status\G
...
MySQL thread id 1087, OS thread handle 139830446065408, query id 7300 localhost root update
insert into l values (3,4,14,20)
------- TRX HAS BEEN WAITING 19 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1358 page no 5 n bits 72 index b of table `test`.`l` trx id 31220594 lock_mode X locks gap before rec insert intention waiting
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     ;;
...
4在被鎖住的範圍以內,因此插不進去

那(1,4,14,20)能插入嗎?先公佈答案,是能夠插入的spa

爲何?
這裏有一個隱晦的問題線程

二級索引排序的時候包含主鍵值排序,鎖的時候範圍也是包含主鍵值,以下:code

((4,2),(6,4)],((6,4),(8,6))

(4,3)在這個範圍內,因此不能插,而(4,1)不在這範圍內,能夠插orm

insert (5,4,14,20)也會阻塞,(4,5)在範圍中排序

Ⅱ、走索引查詢的記錄不存在

2.1 rr事務隔離級別

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

(root@localhost) [test]> select * from l where b = 12 for update;
Empty set (0.00 sec)

(root@localhost) [test]> show engine innodb status\G
...
---TRANSACTION 31220600, ACTIVE 7 sec
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 1104, OS thread handle 139830452774656, query id 7383 localhost root starting
show engine innodb status
TABLE LOCK table `test`.`l` trx id 31220600 lock mode IX
RECORD LOCKS space id 1358 page no 5 n bits 72 index b of table `test`.`l` trx id 31220600 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;
...
heap no 1    0和1的heap no表示的是min和max的記錄,虛擬的
n_fields 1   只有一個列,僞列
key min 4 6 8 10 max

在max上加了鎖,範圍爲(10,正無窮),意味着插入任意大於10的記錄都是不能夠的索引

rr級別時,若是搜索一條記錄搜不到,就會在max上加鎖,意味着這條沒查到的記錄以後的範圍都插不了事務

爲何呢?
若是第一次讀12沒有讀到不把10日後的都鎖住,那一個線程插入了12這條記錄,第二次再去讀就會讀到12,就發生了幻讀

再來
session1:

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

(root@localhost) [test]> select * from l where b = 7 for update;
Empty set (0.00 sec)

(root@localhost) [test]> show engine innodb status\G
···
---TRANSACTION 31220601, ACTIVE 51 sec
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 1104, OS thread handle 139830452774656, query id 7387 localhost root starting
show engine innodb status
TABLE LOCK table `test`.`l` trx id 31220601 lock mode IX
RECORD LOCKS space id 1358 page no 5 n bits 72 index b of table `test`.`l` trx id 31220601 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     ;;
···
在8上面加了一個gap鎖,8自己是不鎖的

session2:

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

(root@localhost) [test]> select * from l where b = 8 for update;
+---+------+------+------+
| a | b    | c    | d    |
+---+------+------+------+
| 6 |    8 |   10 |   12 |
+---+------+------+------+
1 row in set (0.00 sec)
這時候8這條記錄上又有了Next-key Lock鎖,鎖住6到8,8自己也被鎖住,8上面兩把鎖是不抵觸的

2.2 rc事務隔離級別

(root@localhost) [test]> show variables like 'tx_isolation';
+---------------+----------------+
| Variable_name | Value          |
+---------------+----------------+
| tx_isolation  | READ-COMMITTED |
+---------------+----------------+
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 = 12 for update;
Empty set (0.00 sec)

(root@localhost) [test]> show engine innodb status\G
...
------------
TRANSACTIONS
------------
Trx id counter 31220604
Purge done for trx's n:o < 31220593 undo n:o < 0 state: running but idle
History list length 35
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421305875783280, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 421305875781456, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 31220603, ACTIVE 6 sec
1 lock struct(s), heap size 1136, 0 row lock(s)
MySQL thread id 1106, OS thread handle 139830446065408, query id 7436 localhost root starting
show engine innodb status
TABLE LOCK table `test`.`l` trx id 31220603 lock mode IX
...

會發現並無鎖,讀到沒有12這條記錄,直接就釋放了,rc不解決幻讀,這麼看rc的insert併發性能會更好

Ⅲ、不走索引的狀況怎麼鎖?

3.1 rc事務隔離級別

(root@localhost) [test]> show variables like 'tx_isolation';
+---------------+----------------+
| Variable_name | Value          |
+---------------+----------------+
| tx_isolation  | READ-COMMITTED |
+---------------+----------------+
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 d = 10 for update;
+---+------+------+------+
| a | b    | c    | d    |
+---+------+------+------+
| 4 |    6 |    8 |   10 |
+---+------+------+------+
1 row in set (0.00 sec)

(root@localhost) [test]> show engine innodb status\G
...
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421305875783280, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 421305875781456, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 31220604, ACTIVE 11 sec
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 1106, OS thread handle 139830446065408, query id 7446 localhost root starting
show engine innodb status
TABLE LOCK table `test`.`l` trx id 31220604 lock mode IX
RECORD LOCKS space id 1358 page no 3 n bits 72 index PRIMARY of table `test`.`l` trx id 31220604 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     ;;
...

會產生一個記錄鎖,對d=10這條記錄對應的主鍵加鎖

3.2 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 d = 10 for update;
+---+------+------+------+
| a | b    | c    | d    |
+---+------+------+------+
| 4 |    6 |    8 |   10 |
+---+------+------+------+
1 row in set (0.00 sec)

(root@localhost) [test]> show engine innodb status\G
...
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421305875783280, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 421305875781456, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 31220606, ACTIVE 22 sec
2 lock struct(s), heap size 1136, 5 row lock(s)
MySQL thread id 1106, OS thread handle 139830446065408, query id 7459 localhost root starting
show engine innodb status
TABLE LOCK table `test`.`l` trx id 31220606 lock mode IX
RECORD LOCKS space id 1358 page no 3 n bits 72 index PRIMARY of table `test`.`l` trx id 31220606 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

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     ;;

Record lock, heap no 4 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
 0: len 4; hex 80000006; asc     ;;
 1: len 6; hex 000001c1b93f; asc      ?;;
 2: len 7; hex e40000015d0110; asc     ]  ;;
 3: len 4; hex 80000008; asc     ;;
 4: len 4; hex 8000000a; asc     ;;
 5: len 4; hex 8000000c; asc     ;;

Record lock, heap no 5 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
 0: len 4; hex 80000008; asc     ;;
 1: len 6; hex 000001c1b940; asc      @;;
 2: len 7; hex e50000015f0110; asc     _  ;;
 3: len 4; hex 8000000a; asc     ;;
 4: len 4; hex 8000000c; asc     ;;
 5: len 4; hex 8000000e; asc     ;;
...

Next-key Lock鎖住了主鍵的2,4,6,8

(負無窮,2],(2,4],(4,6],(6,8],(8,正無窮)

這並非表鎖(沒有表升級),只是表現形式相似整個鎖住,若是表有100w記錄,會產生100w個lock,鎖模式是Next-key Locking,任何記錄的插入和更新都是不能夠的,鎖的代價很大

由於後面插入任何一條記錄均可以是d=10,因此任何一條記錄都要被鎖住,隨便插一個d=10的記錄都給插那就亂套了,又幻讀了,那就不容許任何記錄插入吧

tips: rr自己不解決幻讀,只有在序列化的事務隔離級別之下才解決幻讀,可是MySQL的鎖比較特殊,在rr隔離級別下就解決幻讀

相關文章
相關標籤/搜索