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)在範圍中排序
(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上面兩把鎖是不抵觸的
(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併發性能會更好
(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這條記錄對應的主鍵加鎖
(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隔離級別下就解決幻讀