浅析 innodb next-key lock

本文深入探讨了InnoDB表锁机制中的三种锁类型:记录锁、区间锁与Next-Key锁。通过实例演示,详细解释了每种锁的作用与实现方式,特别是Next-Key锁的特性及其对幻读的防范作用。
record lock:记录锁,也就是仅仅锁着单独的一行
gap lock:区间锁,仅仅锁住一个区间(注意这里的区间都是开区间,也就是不包括边界值,至于为什么这么定义?innodb官方定义的)
next-key lock:record lock+gap lock,所以next-key lock也就半开半闭区间,且是下界开,上界闭。(为什么这么定义?innodb官方定义的)
下面来举个手册上的例子看什么是next-key lock。假如一个索引的行有10,11,13,20
那么可能的next-key lock的包括:
(无穷小, 10]
(10,11]
(11,13]
(13,20]
(20, 无穷大) (这里无穷大为什么不是闭合?你数学不到家~~)
好了现在通过举例子说明:

表test
mysql> show create table test;
+-------+--------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+--------------------------------------------------------------------------------------------------------+
| test | CREATE TABLE `test` (
`a` int(11) NOT NULL,
PRIMARY KEY (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-------+--------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> select * from test;
+----+
| a |
+----+
| 11 |
| 12 |
| 13 |
| 14 |
+----+
4 rows in set (0.00 sec)
开始实验:
(一)
session 1:

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

mysql> delete from test where a=11;
Query OK, 1 row affected (0.00 sec)

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

mysql> insert into test values(10);
Query OK, 1 row affected (0.00 sec)

mysql> insert into test values(15);
Query OK, 1 row affected (0.00 sec)

mysql> insert into test values(9);
Query OK, 1 row affected (0.00 sec)

mysql> insert into test values(16);
Query OK, 1 row affected (0.01 sec)

mysql> rollback;
Query OK, 0 rows affected (0.00 sec)

ok,上面的情况是预期的,因为a上有索引,那么当然就只要锁定一行,所以其他行的插入不会被阻塞。
那么接下来的情况就有意思了
(二)
session 1
(跟上一个session 1相同):
delete from test where a=22;
Query OK, 0 rows affected (0.01 sec)

session 2:
mysql> insert into test values (201);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (20);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (19);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (18);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (16);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (9);
Query OK, 1 row affected (0.00 sec)

从上面的结果来看,在a=11后面所有的行,也就是区间(11,无穷大)都被锁定了。先不解释原因,再来看一种情况:
(三)
session 1:

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

mysql> select * from test;
+----+
| a |
+----+
| 7 |
| 9 |
| 10 |
| 12 |
| 13 |
| 14 |
| 15 |
| 22 |
| 23 |
| 24 |
| 25 |
+----+
11 rows in set (0.00 sec)

mysql> delete from test where a=21;
Query OK, 0 rows affected (0.00 sec)

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

mysql> insert into test values (20);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (26);
Query OK, 1 row affected (0.00 sec)

mysql> insert into test values (21);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (16);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (6);
Query OK, 1 row affected (0.01 sec)

从这里可以看出,现在被锁住的区间就只有[16,21)了。
有了前面对三种类型的加锁解释,现在可以来解释为什么会这样了,在innodb表中 delete from where ..针对扫描到的索引记录加next-key锁(具体的什么语句加什么锁可以查看手册,另外需要说明一下,行锁加锁对象永远是索引记录,因为innodb中表即索引) 。
在(一)中,实际上加的next-key lock就是(11,11] 因此也只有a=11这一条记录被锁住,其他所有插入都没有关系。
在(二)中,因为a=22这条记录不存在,而且22比表里所有的记录值都大,所以在innodb看来锁住的区间就是(14, 无穷大)。所以在插入14以后的值都提示被锁住,而14之前的则可以。
在(三)种,a=21也是不存在,但是在表里面21前后都有记录,因此这里next-key lock的区间也就是(15,21],因此不在这个区间内的都可以插入。
那么为什么next-key lock都是下界开区间,上界闭区间呢?这个倒不重要,管它呢,但是有一点我个人却觉得比较怪,比如说
delete test where a > 11 #------- 1
它的next-key lock是(11, 无穷大)
delete test where a < 11 #------- 2
它的next-key lock是(无穷小, 10]
这样给人的感觉就很怪,因为在手册上对next-key lock的定义:
Next-key lock: This is a combination of a record lock on the index record and a gap lock on the gapbefore the index record.
而在1那种情况下,如果按照手册上的解释,记录锁和它之前的gap那么就会有些牵强。[今天再次看了一遍官方手册,是之前自己的理解不到位,这个before是对的,因为innodb在加锁时是所有扫描过程中遇到的记录都会被加锁,那么对于1那种情况,实际上是从12开始扫描,但是因为要保证a>11的都被delete掉,因此得一直扫描下去那自然最大值就是无穷大,因为这个next-key lock就是无穷大这条记录(这是假设的一条记录,表示一个边界)加上它之前的gap lock (11, 无穷大),所以在任何时候next-lock都是record lock加上这个record之前的一个gap lock]
但是只要我们自己能理解就行了:记录锁---锁单条记录;区间锁---锁一个开区间;next-key 锁---前面两者的结合,而不要管什么before。
另外next-key lock虽然在很多时候是锁一个区间,但要明白一个区间也可能只有一个元素,因此在称delete from tb where key=x 这种情况下加next-key锁也是完全正确的。

另外还提两点:
1.如果我们的SQL语句里面没有利用到索引,那么加锁对象将是所有行(但不是加表锁),所以建索引是很重要的

2.next-key lock是为防止幻读的发生,而只有repeatable-read以及以上隔离级别才能防止幻读,所以在read-committed隔离级别下面没有next-key lock这一说法。




参考文章:
[1] mysql手册
[2] http://www.mysqlperformanceblog.com/2012/03/27/innodbs-gap-locks/
<think>好的,我现在需要回答用户关于MySQL中页级锁和Next-Key Lock的区别及应用场景的问题。首先,我需要明确这两个概念的定义和它们在InnoDB中的角色。 首先,页级锁,根据我之前学过的知识,页级锁是MySQL中一种锁定机制,锁定整个数据页。数据页是InnoDB存储数据的基本单位,通常大小为16KB。页级锁的粒度介于表锁和行锁之间,它会影响整个页内的所有行,即使只访问其中一行。这种锁的优点是开销比行锁小,冲突概率比表锁低,但缺点是不够精细,可能导致不必要的锁定,影响并发性能。应用场景可能是在需要平衡并发和锁开销的情况下,比如批量更新同一页内的多行数据时。 然后是Next-Key Lock,这是InnoDB为了实现可重复读隔离级别而设计的锁机制,结合了记录锁(行锁)和间隙锁。Next-Key Lock锁定的是索引记录之间的间隙以及记录本身,防止幻读的发生。例如,当查询某个范围的数据时,Next-Key Lock会锁定这个范围以及相邻的间隙,确保其他事务不能插入新的数据到这个范围内。根据引用[4]提到的加锁规则,Next-Key Lock是前开后闭区间,并且在某些情况下会退化为行锁或间隙锁。 接下来,我需要区分两者的区别。首先是锁的粒度:页级锁锁定整个页,而Next-Key Lock锁定的是索引记录和间隙,属于行级锁的范畴。锁的用途方面,页级锁用于控制对物理页的访问,而Next-Key Lock用于解决幻读问题,保证事务的隔离性。在并发控制上,页级锁可能因为锁住更多数据而导致并发度较低,而Next-Key Lock虽然更精细,但由于需要维护更多锁,可能有更高的开销。应用场景方面,页级锁适合批量操作同一页的数据,而Next-Key Lock用于需要防止幻读的高并发事务环境。 还需要考虑用户提供的引用内容。引用[1]提到InnoDB的三种行锁:间隙锁、行锁和Next-Key Lock,说明Next-Key Lock属于行级锁的一部分。引用[4]详细说明了Next-Key Lock的加锁规则,包括退化为行锁或间隙锁的情况,这有助于解释其灵活性。而页级锁并不属于行锁,是另一种粒度的锁,可能在较早的MySQL版本或特定场景中使用较多,但现在InnoDB默认使用行级锁和Next-Key Lock来实现更高并发。 用户可能混淆了不同粒度的锁,需要明确页级锁和行级锁(包括Next-Key Lock)的不同层级。此外,应用场景的对比需要具体举例,比如页级锁在批量处理时的效率,而Next-Key Lock在事务安全中的重要性。 最后,确保回答结构清晰,分点说明概念、区别、应用场景,并生成相关问题。同时检查是否需要引用,比如引用[4]提到的加锁规则,可以在解释Next-Key Lock时引用。</think>### 页级锁与Next-Key Lock的概念及区别 #### 一、基本概念 1. **页级锁** - **定义**:锁定InnoDB存储引擎中的物理数据页(默认16KB),属于中粒度锁。 - **特点**:无论访问页内多少行数据,都会锁定整个页。 - **应用场景**:适用于批量操作同一页内多行数据的场景(如旧版本MySQL的特定优化)。 2. **Next-Key Lock** - **定义**:InnoDB在可重复读(Repeatable Read)隔离级别下使用的锁机制,**结合记录锁(行锁)和间隙锁**,防止幻读[^2][^4]。 - **特点**: - 锁定范围:索引记录本身(行锁) + 索引前的间隙(间隙锁),形成**前开后闭区间**,例如`(5, 10]`[^4]。 - 根据查询条件可能退化为**行锁**(唯一索引等值查询)或**间隙锁**(非唯一索引等值查询)[^4]。 --- #### 二、核心区别 | **维度** | **页级锁** | **Next-Key Lock** | |-------------------|--------------------------------|-----------------------------------| | **锁粒度** | 物理页(16KB) | 索引记录及间隙(行级锁范畴) | | **锁目标** | 控制物理页的并发访问 | 解决幻读,保证事务隔离性 | | **并发性能** | 较低(锁定更多数据) | 较高(更精细,但锁数量可能更多) | | **应用场景** | 批量操作同一页数据 | 高并发事务中防止幻读 | | **实现层级** | 存储引擎物理层 | 索引逻辑层 | --- #### 三、应用场景 1. **页级锁** - **适用场景**: - 历史版本MySQL中需要平衡锁开销与并发性能的场景。 - 批量更新或删除同一页内的连续数据(如日志表清理)。 - **局限性**:现代InnoDB默认使用行级锁,页级锁逐渐被替代。 2. **Next-Key Lock** - **适用场景**: - 事务需要避免幻读(如银行转账、库存扣减)。 - 范围查询(`WHERE id BETWEEN 10 AND 20`)或非唯一索引的等值查询。 - **示例**: ```sql -- 对索引列age=20的查询会锁定(15,20]的Next-Key Lock SELECT * FROM users WHERE age = 20 FOR UPDATE; ``` --- #### 四、总结对比 - **页级锁**关注**物理存储单元**的并发控制,适合**粗粒度操作**。 - **Next-Key Lock**关注**逻辑数据一致性**,通过精细的行级锁实现事务安全。 - **现代InnoDB更依赖Next-Key Lock**,因其能有效支持高并发事务场景。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值