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 用于搜索和索引扫描,可以防止幻读