InnoDB中鎖的算法(3)

Ⅰ、隱式鎖vs顯示鎖

session1:mysql

(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]> insert into l values (16,18,20,22);
Query OK, 1 row affected (0.00 sec)

(root@localhost) [test]> show engine innodb status\G
...
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421305875781456, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 31220665, ACTIVE 24 sec
1 lock struct(s), heap size 1136, 0 row lock(s), undo log entries 1
MySQL thread id 1185, OS thread handle 139830020065024, query id 7781 localhost root starting
show engine innodb status
TABLE LOCK table `test`.`l` trx id 31220665 lock mode IX
...

會發現插入的這條記錄上沒有鎖,只能看到一把意向鎖sql

session2:session

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

(root@localhost) [test]> select * from l where a = 16 for update;
hang~~~  ???

session1:併發

(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 31220670, ACTIVE 18 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 1184, OS thread handle 139830453040896, query id 7783 localhost root statistics
select * from l where a = 16 for update
------- TRX HAS BEEN WAITING 18 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1358 page no 3 n bits 80 index PRIMARY of table `test`.`l` trx id 31220670 lock_mode X locks rec but not gap waiting
Record lock, heap no 7 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
 0: len 4; hex 80000010; asc     ;;
 1: len 6; hex 000001dc63b9; asc     c ;;
 2: len 7; hex b4000001a10110; asc        ;;
 3: len 4; hex 80000012; asc     ;;
 4: len 4; hex 80000014; asc     ;;
 5: len 4; hex 80000016; asc     ;;

------------------
TABLE LOCK table `test`.`l` trx id 31220670 lock mode IX
RECORD LOCKS space id 1358 page no 3 n bits 80 index PRIMARY of table `test`.`l` trx id 31220670 lock_mode X locks rec but not gap waiting
Record lock, heap no 7 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
 0: len 4; hex 80000010; asc     ;;
 1: len 6; hex 000001dc63b9; asc     c ;;
 2: len 7; hex b4000001a10110; asc        ;;
 3: len 4; hex 80000012; asc     ;;
 4: len 4; hex 80000014; asc     ;;
 5: len 4; hex 80000016; asc     ;;

---TRANSACTION 31220665, ACTIVE 252 sec
2 lock struct(s), heap size 1136, 1 row lock(s), undo log entries 1
MySQL thread id 1185, OS thread handle 139830020065024, query id 7781 localhost root
TABLE LOCK table `test`.`l` trx id 31220665 lock mode IX
RECORD LOCKS space id 1358 page no 3 n bits 80 index PRIMARY of table `test`.`l` trx id 31220665 lock_mode X locks rec but not gap
Record lock, heap no 7 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
 0: len 4; hex 80000010; asc     ;;
 1: len 6; hex 000001dc63b9; asc     c ;;
 2: len 7; hex b4000001a10110; asc        ;;
 3: len 4; hex 80000012; asc     ;;
 4: len 4; hex 80000014; asc     ;;
 5: len 4; hex 80000016; asc     ;;
...

這裏能夠發現這條記錄上的鎖又出來了,爲何?性能

緣由:innodb作了優化,這個鎖叫隱式鎖,這條記錄不須要加鎖就知道上面有鎖,由於這條記錄對應的事務還在事務活躍列表中優化

  • 顯式鎖(explicit-lock)
    select * from t where rowd = xxx for update;
  • 隱式鎖(implicit-lock)
    不建立鎖對象若沒有鎖衝突,發生等待則轉化爲顯示鎖,這樣鎖的開銷就進一步降低了,幾乎不多

小結:
insert操做一開始是隱式鎖,不建立鎖對象,發生等待的時候才轉化爲顯式鎖,查到a=16這條記錄在活躍事務列表中,就是沒提交,說明上面有鎖,這時候建立鎖對象,即延遲建立鎖對象,若是在延遲過程當中,沒有對這條記錄加鎖,就不用建立鎖對象,這樣就節省內存了spa

Ⅱ、插入意向鎖

session1:線程

(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]> select * from l;
+----+------+------+------+
| a  | b    | c    | d    |
+----+------+------+------+
|  2 |    4 |    6 |    8 |
|  4 |    6 |    8 |   10 |
|  6 |    8 |   10 |   12 |
|  8 |   10 |   12 |   14 |
| 10 |   12 |   14 |   16 |
| 20 |   22 |   24 |   26 |
+----+------+------+------+
6 rows in set (0.00 sec)

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

(root@localhost) [test]> select * from l where a < 20 for update;
+----+------+------+------+
| a  | b    | c    | d    |
+----+------+------+------+
|  2 |    4 |    6 |    8 |
|  4 |    6 |    8 |   10 |
|  6 |    8 |   10 |   12 |
|  8 |   10 |   12 |   14 |
| 10 |   12 |   14 |   16 |
+----+------+------+------+
5 rows in set (0.00 sec)

session2:code

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

(root@localhost) [test]> insert into l values (14 ,16, 18, 20);
~~~

session3:orm

(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 31220676, ACTIVE 27 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 1184, OS thread handle 139830453040896, query id 7811 localhost root update
insert into l values (14 ,16, 18, 20)
------- TRX HAS BEEN WAITING 27 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1358 page no 3 n bits 80 index PRIMARY of table `test`.`l` trx id 31220676 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 7 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
 0: len 4; hex 80000014; asc     ;;
 1: len 6; hex 000001dc63c1; asc     c ;;
 2: len 7; hex ba000001970110; asc        ;;
 3: len 4; hex 80000016; asc     ;;
 4: len 4; hex 80000018; asc     ;;
 5: len 4; hex 8000001a; asc     ;;

------------------
TABLE LOCK table `test`.`l` trx id 31220676 lock mode IX
RECORD LOCKS space id 1358 page no 3 n bits 80 index PRIMARY of table `test`.`l` trx id 31220676 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 7 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
 0: len 4; hex 80000014; asc     ;;
 1: len 6; hex 000001dc63c1; asc     c ;;
 2: len 7; hex ba000001970110; asc        ;;
 3: len 4; hex 80000016; asc     ;;
 4: len 4; hex 80000018; asc     ;;
 5: len 4; hex 8000001a; asc     ;;

---TRANSACTION 31220675, ACTIVE 75 sec
2 lock struct(s), heap size 1136, 6 row lock(s)
MySQL thread id 1185, OS thread handle 139830020065024, query id 7809 localhost root
TABLE LOCK table `test`.`l` trx id 31220675 lock mode IX
RECORD LOCKS space id 1358 page no 3 n bits 80 index PRIMARY of table `test`.`l` trx id 31220675 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     ;;

篇幅緣由省略下面不相關記錄鎖
...

這時候能看到插入意向鎖了

gap before rec insert intention waiting

session1:

(root@localhost) [test]> commit;
Query OK, 0 rows affected (0.01 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 421305875782368, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 31220677, ACTIVE 17 sec
2 lock struct(s), heap size 1136, 1 row lock(s), undo log entries 1
MySQL thread id 1184, OS thread handle 139830453040896, query id 7815 localhost root
TABLE LOCK table `test`.`l` trx id 31220677 lock mode IX
RECORD LOCKS space id 1358 page no 3 n bits 80 index PRIMARY of table `test`.`l` trx id 31220677 lock_mode X locks gap before rec insert intention
Record lock, heap no 7 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
 0: len 4; hex 80000014; asc     ;;
 1: len 6; hex 000001dc63c1; asc     c ;;
 2: len 7; hex ba000001970110; asc        ;;
 3: len 4; hex 80000016; asc     ;;
 4: len 4; hex 80000018; asc     ;;
 5: len 4; hex 8000001a; asc     ;;
...

能夠看到,對20這條記錄加了一個gap鎖,可是是insert intention的

睜大眼睛啊
session1:

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

(root@localhost) [test]> insert into l values (15, 17, 19, 20);
Query OK, 1 row affected (0.00 sec)

哦嚯,插入成功,美雞雞

Ⅲ、捋一下爲何能夠插?

a列:2 4 6 8 10 20
step1:
20這條記錄上有一個X鎖,next-key-locking,鎖住xxx...(10,20]這幾個範圍

step2:
插入14這條記錄,會對20這條記錄加一個gap鎖,即(14,20),可是這個gap鎖有個insert intention的屬性

step3:
第一個事務commit,事務2持有了上面這把(14,20)的insert intention的gap鎖
這時候插15是能插入的,就由於insert intention

gap鎖是用來阻塞的,以前的理解(14,20)之間是不能插入15的,可是有了上面說的這個特性,就表示插入非阻塞,即容許插入,意義在於提高了插入性能

若是沒有insert intention,那插入14時(14,20)上面就是加一個gap鎖,事務1提交則事務2獲取這個gap鎖,插入15,是插不了的,性能降低了

tips:
插入14地時候爲何這裏會阻塞呢?由於14要在20上加一個gap鎖,爲何要加gap鎖來判斷到底能不能插,一條記錄能不能插就看它後面這條記錄上有沒有鎖,這個鎖是否是gap的,若是是那就不能插,只是一個record鎖那就能插,而這個例子20這條記錄上自己是有gap的因此就等待了

總結:

  • insert intention用來判斷當前事務可否插入,並不阻塞後面其餘線程在這個範圍的插入操做,提高了併發插入的性能
  • gap insert intention互相之間自己是兼容的
  • insert在等待的時候(被阻塞)纔會加gap insert intention鎖,不等待是沒任何鎖的
  • rc沒有next-key-lock鎖,沒有上面的狀況,鎖住20表示只鎖住記錄自己,沒有鎖住一個範圍,14是能夠直接插的
相關文章
相關標籤/搜索