Next-Key Lock实战探究

探究原因

默认情况下,InnoDB工作在可重复读隔离级别下,并且会以Next-Key Lock的方式对数据行进行加锁。而我目前使用的数据库引擎基本都是InnoDB,所以有必要对此引擎的默认加锁方式进行探究。

官方描述

在这里插入图片描述
在这里插入图片描述

探究使用的数据库建表语句 以及数据初始化sql

CREATE TABLE base_goods (
  id bigint(20) NOT NULL DEFAULT '0',
  goods_code varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '商品表',
  goods_name varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '商品名称',
  price decimal(16,4) DEFAULT NULL COMMENT '价格',
  ynflag bit(1) DEFAULT NULL COMMENT '是否启用 0否 1是',
  create_user varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建人',
  create_time timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  update_time timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
  update_user varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '修改人',
  version_num int(11) DEFAULT NULL COMMENT '版本号',
  PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='商品表';

INSERT INTO `base_goods` (`id`, `goods_code`, `goods_name`, `price`, `ynflag`, `create_user`, `create_time`, `update_time`, `update_user`, `version_num`) VALUES (1, '1', '戴尔外星人', 1.0000, '', 'yaksha', '2020-5-25 14:49:01', '2020-5-25 14:49:01', 'yaksha', 0);
INSERT INTO `base_goods` (`id`, `goods_code`, `goods_name`, `price`, `ynflag`, `create_user`, `create_time`, `update_time`, `update_user`, `version_num`) VALUES (2, '2', '华为p30', 1.0000, '', 'yaksha', '2020-5-25 15:09:00', '2020-5-25 15:09:00', 'yaksha', 0);
INSERT INTO `base_goods` (`id`, `goods_code`, `goods_name`, `price`, `ynflag`, `create_user`, `create_time`, `update_time`, `update_user`, `version_num`) VALUES (5, '5', '苹果10x', 1.0000, '', 'yaksha', '2020-5-25 15:14:27', '2020-5-25 15:14:27', 'yaksha', 0);
INSERT INTO `base_goods` (`id`, `goods_code`, `goods_name`, `price`, `ynflag`, `create_user`, `create_time`, `update_time`, `update_user`, `version_num`) VALUES (7, '7', '小米鞋', 2.0000, '', 'yaksha', '2020-5-25 19:14:30', '2020-5-25 19:14:30', 'yaksha', 0);
INSERT INTO `base_goods` (`id`, `goods_code`, `goods_name`, `price`, `ynflag`, `create_user`, `create_time`, `update_time`, `update_user`, `version_num`) VALUES (8, '8', '妈个鸡键盘', 3.0000, '', 'yaksha', '2020-5-25 19:22:09', '2020-5-25 19:22:09', 'yaksha', 0);
INSERT INTO `base_goods` (`id`, `goods_code`, `goods_name`, `price`, `ynflag`, `create_user`, `create_time`, `update_time`, `update_user`, `version_num`) VALUES (9, '9', '老六你是共产党', 6.0000, '', 'yaksha', '2020-6-15 14:59:28', '2020-6-15 14:59:28', 'yaksha', 0);
INSERT INTO `base_goods` (`id`, `goods_code`, `goods_name`, `price`, `ynflag`, `create_user`, `create_time`, `update_time`, `update_user`, `version_num`) VALUES (13, '13', '13', 8.0000, '', 'yaksha', '2020-10-15 22:48:16', '2020-10-15 22:48:16', 'yaksha', 0);
INSERT INTO `base_goods` (`id`, `goods_code`, `goods_name`, `price`, `ynflag`, `create_user`, `create_time`, `update_time`, `update_user`, `version_num`) VALUES (17, '17', '9', 9.0000, '', 'yaksha', '2020-10-15 22:57:23', '2020-10-15 22:57:23', 'yaksha', 0);
INSERT INTO `base_goods` (`id`, `goods_code`, `goods_name`, `price`, `ynflag`, `create_user`, `create_time`, `update_time`, `update_user`, `version_num`) VALUES (19, '21', '21', 10.0000, '', 'yaksha', '2020-10-15 23:19:23', '2020-10-15 23:19:23', 'yaksha', 0);
INSERT INTO `base_goods` (`id`, `goods_code`, `goods_name`, `price`, `ynflag`, `create_user`, `create_time`, `update_time`, `update_user`, `version_num`) VALUES (25, '27', '27', 11.0000, '', 'yaksha', '2020-10-15 23:27:28', '2020-10-15 23:27:28', 'yaksha', 0);

初始化后表内数据
在这里插入图片描述

探究配置

每次进行探究的时候,数据都会还原为执行完建表语句和初始化sql之后的状态。

探究1(通过不带索引的字段查找数据)

开启一个事务,执行下列语句查看锁的情况

START TRANSACTION;
select * from base_goods t where t.goods_code = '5' for UPDATE;

查询锁的情况

select * from performance_schema.data_locks where OBJECT_SCHEMA ='bak_xcx'; 

此表的相关列解释详见mysql官网
https://dev.mysql.com/doc/refman/8.0/en/performance-schema-data-locks-table.html
在这里插入图片描述

可以看出来是bak_xcx数据库中的base_goods表中存在锁,分别是table(表)级别的IX(意向排他锁和)RECORD(行)级别的X(排他锁),锁的间隙节点数据保存再列LOCK_DATA中(其中的supremum pseudo-record为最大值间隙)
对比表中数据也可得出此时的锁真实锁的数据是整张表。即如果通过未加索引的列进行上锁时锁的将是整张表。

探究2(通过带索引的字段查找能命中数据)

先给goods_code列加上索引

	ALTER TABLE `base_goods` ADD INDEX INDEX_CODE ( `goods_code` ) ;

开启一个事务,执行下列语句查看锁的情况

START TRANSACTION;
select * from base_goods t where t.goods_code = '5' for UPDATE;

查询锁的情况

select * from performance_schema.data_locks where OBJECT_SCHEMA ='bak_xcx'; 

在这里插入图片描述
上锁的数据为goods_code为“5”(主键为5)的间隙锁和id为5的行锁,以及goods_code为“7”(主键为7)的间隙锁。此时锁住的数据为goods_code为“2”id为2到goods_code为“7”id为7的间隙即goods_code的(2,7],因为b+tree的结构导致id在(2,7)内的goods_code为2或者7的数据也插入不进去。
而又因为mysql的优化
索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock退化为间隙锁 所以此时锁的间隙改为goods_code的(2,7),id的(2,7)。

下面是相关间隙数据插入测试的情况

INSERT INTO `base_goods` VALUES ( 0, '2', '周末吃火锅', 3.0000, '', 'yaksha', '2020-5-25 19:22:09', '2020-5-25 19:22:09', 'yaksha', 0 ); -- 成功,脱离了id和goods_code间隙
INSERT INTO `base_goods` VALUES ( 4, '3', '周末吃火锅', 3.0000, '', 'yaksha', '2020-5-25 19:22:09', '2020-5-25 19:22:09', 'yaksha', 0 ); -- 失败,属于goods_code间隙和id间隙
INSERT INTO `base_goods` VALUES ( 6, '7', '周末吃火锅', 3.0000, '', 'yaksha', '2020-5-25 19:22:09', '2020-5-25 19:22:09', 'yaksha', 0 );-- 失败,属于id间隙
INSERT INTO `base_goods` VALUES ( 10, '7', '周末吃火锅', 3.0000, '', 'yaksha', '2020-5-25 19:22:09', '2020-5-25 19:22:09', 'yaksha', 0 );-- 成功 ,脱离了id和goods_code间隙
	
<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**,因其能有效支持高并发事务场景。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值