使用next-key locks 用于搜索和索引扫描,可以防止幻读

Next-Key Locks

A next-key lock is a combination of a record lock on the index record and a gap lock on the gap before the index 

record. 

是一个记录锁在索引记录上和一个区间锁在区间的组合 在index record之前



InnoDB 执行行级锁以这种方式 当它搜索或者扫描一个表的索引,它设置共享或者排它锁在Index records。

因此, row-level locks 实际上是 index-record locks.


一个 next-key lock 在一个index record 也会影响 那个index record 记录前的gap


也就是说 一个next-key lock 是一个 index-record lock plus a gap lock 


If one session has a shared or exclusive lock on record R in an index, another session cannot insert a new index 

record in the gap immediately before R in the index order. 

如果一个session 有一个共享或者排它锁在记录R上,另外的session 不能插入新的index record 在before R这个区间


Session 1:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> update SmsTest set phoneNo=111 where  phoneNo <10;
Query OK, 20 rows affected (0.01 sec)
Rows matched: 20  Changed: 20  Warnings: 0

mysql> explain  update SmsTest set phoneNo=111 where  phoneNo <10;
+----+-------------+---------+-------+---------------+--------------+---------+-------+------

+------------------------------+
| id | select_type | table   | type  | possible_keys | key          | key_len | ref   | rows | Extra                  

      |
+----+-------------+---------+-------+---------------+--------------+---------+-------+------

+------------------------------+
|  1 | SIMPLE      | SmsTest | range | SmsTest_idx1  | SmsTest_idx1 | 4       | const |   20 | Using where; Using 

temporary |
+----+-------------+---------+-------+---------------+--------------+---------+-------+------

+------------------------------+
1 row in set (0.00 sec)


锁住1-9的记录


mysql> select * from SmsTest  where  phoneNo <10;
+-------+---------+-------------+--------+
| sn    | phoneNo | channelType | status |
+-------+---------+-------------+--------+
|     1 |       1 |           2 |      1 |
| 45210 |       1 |           1 |      1 |
| 45211 |       1 |           1 |      1 |
|     2 |       2 |           2 |      1 |
|   201 |       2 |           1 |      1 |
| 45212 |       2 |           1 |      1 |
|     3 |       3 |           2 |      1 |
| 45209 |       3 |           1 |      1 |
| 45213 |       3 |           1 |      1 |
|     4 |       4 |           2 |      1 |
| 45214 |       4 |           1 |      1 |
|     5 |       5 |           2 |      1 |
| 45215 |       5 |           1 |      1 |
|     6 |       6 |           2 |      1 |
| 45216 |       6 |           1 |      1 |
|     7 |       7 |           2 |      1 |
| 45217 |       7 |           1 |      1 |
|     8 |       8 |           2 |      1 |
| 45218 |       8 |           1 |      1 |
|     9 |       9 |           2 |      1 |
+-------+---------+-------------+--------+
20 rows in set (0.00 sec)


Session 2:


mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(1,1,1); --hang

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(2,1,1);  --hang

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(3,1,1);  --hang

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(4,1,1); --hang

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(5,1,1); --hang

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(6,1,1);  --hang

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(7,1,1); --hang

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(8,1,1); --hang

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(9,1,1); --hang


mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(10,1,1);
Query OK, 1 row affected (0.01 sec)

mysql> 
mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(11,1,1);
Query OK, 1 row affected (0.00 sec)

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(12,1,1);
Query OK, 1 row affected (0.01 sec)


假设一个Index 包含值10,11,13和20,可能的next-key locks 对于这个索引覆盖下面的区间,

一个圆括号表示排除两端,一个方块号报表包含

(negative infinity, 10] 负无穷大
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)正无穷大


测试:
mysql> select * from SmsTest where phoneNo in (10,11,13,20);
+-------+---------+-------------+--------+
| sn    | phoneNo | channelType | status |
+-------+---------+-------------+--------+
|    10 |      10 |           2 |      1 |
| 45239 |      10 |           1 |      1 |
| 45252 |      10 |           1 |      1 |
|    11 |      11 |           2 |      1 |
| 45253 |      11 |           1 |      1 |
|    13 |      13 |           2 |      1 |
|    20 |      20 |           2 |      1 |
+-------+---------+-------------+--------+
7 rows in set (0.00 sec)



mysql> update SmsTest set phoneNo=999 where  phoneNo in (10,11,13,20);
Query OK, 7 rows affected (0.00 sec)
Rows matched: 7  Changed: 7  Warnings: 0

mysql> explain update SmsTest set phoneNo=999 where  phoneNo in (10,11,13,20);
+----+-------------+---------+-------+---------------+--------------+---------+-------+------

+------------------------------+
| id | select_type | table   | type  | possible_keys | key          | key_len | ref   | rows | Extra                  

      |
+----+-------------+---------+-------+---------------+--------------+---------+-------+------

+------------------------------+
|  1 | SIMPLE      | SmsTest | range | SmsTest_idx1  | SmsTest_idx1 | 4       | const |    7 | Using where; Using 

temporary |
+----+-------------+---------+-------+---------------+--------------+---------+-------+------

+------------------------------+
1 row in set (0.00 sec)



Session 2:

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(1,1,1);
Query OK, 1 row affected (0.01 sec)

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(2,1,1);
Query OK, 1 row affected (0.00 sec)

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(3,1,1);
Query OK, 1 row affected (0.05 sec)

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(4,1,1);
Query OK, 1 row affected (0.01 sec)

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(5,1,1);
Query OK, 1 row affected (0.00 sec)

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(6,1,1);
Query OK, 1 row affected (0.00 sec)

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(7,1,1);
Query OK, 1 row affected (0.01 sec)

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(8,1,1);
Query OK, 1 row affected (0.00 sec)

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(9,1,1); --hang

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(10,1,1);--hang

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(11,1,1); --hang

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(12,1,1); --hang

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(13,1,1);--hang

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(14,1,1);
Query OK, 1 row affected (0.01 sec)

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(15,1,1);
Query OK, 1 row affected (0.00 sec)

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(16,1,1);
Query OK, 1 row affected (0.00 sec)

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(17,1,1);
Query OK, 1 row affected (0.00 sec)

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(18,1,1);
Query OK, 1 row affected (0.00 sec)

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(19,1,1);

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(20,1,1); --hang


mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(21,1,1);
Query OK, 1 row affected (0.01 sec)

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(22,1,1);
Query OK, 1 row affected (0.00 sec)

mysql>  insert into zjzc.SmsTest(PhoneNo,channelType,status)  values(23,1,1);
Query1 row   affected (0.01 sec)

默认情况下,InnoDB 操作在 REPEATABLE READ transaction isolation level  禁用innodb_locks_unsafe_for_binlog system 

variable 

在这种情况下,使用next-key locks 用于搜索和索引扫描,可以防止幻读

posted @ 2016-10-19 17:44  czcb  阅读(141)  评论(0编辑  收藏  举报