mysql间隙锁

本文详细介绍了MySQL中的间隙锁(NEXT-KEY锁)机制,包括其基本概念、产生方式和作用。通过举例说明,阐述了间隙锁如何防止幻读和数据误删/改。同时,也指出了间隙锁可能导致的性能问题,特别是在高并发插入场景下可能带来的影响。

一、间隙锁的基本概念

1.什么叫间隙锁

当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(NEXT-KEY)锁。

2.间隙锁的产生

上面的文字很抽象,现在举个栗子,介绍间隙锁是怎么产生的:

假设有以下表t_student:(其中id为PK,name为非唯一索引)

idnamesexaddress
1zhaoyi0beijin
3sunsan1shanghai
4lisi0guangzhou
5zhouwu0shenzhen
6wuliu1hangzhou

这个时候我们发出一条这样的加锁sql语句:

select id,name from t_student where id > 0 and id < 5 for update;

这时候,我们命中的数据为以下着色部分:

idnamesexaddress
1zhaoyi0beijin
3sunsan1shanghai
4lisi0guangzhou
5zhouwu0shenzhen
6wuliu1hangzhou

细心的朋友可能就会发现,这里缺少了条id为2的记录,我们的重点就在这里。

select ... for update这条语句,是会对数据记录加锁的,这里因为命中了索引,加的是行锁。从数据记录来看,这里排它锁锁住数据是id为1、3和4的这3条数据。

但是,看看前面我们的介绍——对于键值在条件范围内但不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加锁。

好了,我们这里,键值在条件范围但是不存在的记录,就是id为2的记录,这里会对id为2数据加上间隙锁。假设这时候如果有id=2的记录insert进来了,是要等到这个事务结束以后才会执行的

二、间隙锁的作用

总的来说,有2个作用:防止幻读和防止数据误删/改

1.防止幻读

关于幻读的概念可以参考我这篇文章 https://blog.youkuaiyun.com/mweibiao/article/details/80805031 ,这里就不多做解释了

假设有下面场景

时间事务A事务B
T1select count(1) from t_student where id > 1; 
T2 insert into t_student values(2,'qianer',1,'nanjing');
T3 commit;
T4select count(1) from t_student where id > 1; 
T5commit; 

如果没有间隙锁,事务A在T1和T4读到的结果是不一样的,有了间隙锁,读的就是一样的了

2.防止数据误删/改

这个作用比较重要,假设以下场景:

时间事务A事务B
T1delete from t_student where id < 4; 
T2 insert into t_student values(2,'qianer',1,'nanjing');
T3 commit;
T4commit; 

这种情况下,如果没有间隙锁,会出现的问题是:id为2的记录,刚加进去,就被删除了,这种情况有时候对业务,是致命性的打击。加了间隙锁之后,由于insert语句要等待事务A执行完之后释放锁,避免了这种情况

三.使用间隙锁的隐患

最大的隐患就是性能问题

前面提到,假设这时候如果有id=2的记录insert进来了,是要等到这个事务结束以后才会执行的,假设是这种场景  

时间事务A事务B
T1select * from t_student where id>1 and id < 100 for update; 
T2 insert into t_student values(2,'qianer',1,'nanjing');
T3update t_student set xxxx where id=xxx; 
T4update t_student set xxxx where id=xxx; 
T5update t_student set xxxx where id=xxx; 
T6 
T7commit; 

这种情况,对插入的性能就有很大影响了,必须等到事务结束才能进行插入,性能大打折扣

更有甚者,如果间隙锁出现死锁的情况下,会更隐晦,更难定位

### MySQL 间隙锁(Gap Lock)原理及应用场景 #### 一、间隙锁的定义与作用 间隙锁是一种用于防止其他事务在指定范围内插入新记录的锁机制。它主要应用于 InnoDB 存储引擎,在涉及范围查询时会自动启用。当使用范围条件而非精确匹配来访问数据时,InnoDB 不仅会对已存在的记录锁,还会对这些记录之间的“间隙”施锁定[^1]。 这种设计的主要目的是为了阻止并发事务向某个特定范围插入新的记录,从而有效避免幻读现象的发生。通过结合间隙锁和记录锁形成 **next-key lock**,可以实现更严格的隔离级别——可重复读 (Repeatable Read)[^3]。 #### 二、间隙锁的工作方式 间隙锁的作用对象是两个相邻索引值之间形成的逻辑间隔区域。例如,在表 `t` 中存在如下索引值序列 `{10, 20, 30}`,那么可能存在的间隙包括 `(负无穷, 10)`、`(10, 20)` 和 `(20, 30)` 等。如果某条 SQL 查询语句指定了一个范围条件,则该操作可能会触发间隙锁的应用: ```sql SELECT * FROM t WHERE id BETWEEN 15 AND 25 FOR UPDATE; ``` 上述例子中,假设当前仅有两条记录分别对应 ID 值为 10 和 30 的情况,执行此命令后不仅会对实际查找到的数据行上排他锁,同时也会覆盖整个 `(10, 30)` 范围内的所有潜在位置设置间隙锁[^2]。 需要注意的是,间隙锁本身并不影响已经存在于数据库中的具体元组实例;它的核心功能在于控制未被占用的空间部分,确保不会因外部干扰而导致一致性破坏。 #### 三、Next-Key Lock 组成分析 正如前面提到过的那样,默认情况下 InnoDB 使用一种称为 Next-Key Lock 的组合形式来进行资源保护。其结构由两部分内容共同构成: - **Record Lock**: 针对确切的目标实体实施独占或者共享权限管理; - **Gap Lock**: 对围绕目标周围的空白领域以约束限制。 因此每一个单独的 Next-Key Lock 实际上代表了一个半开放式的区间表达式 `[start_key, end_key)` ,其中 start_key 表示起始边界点而 end_key 则表示结束界限但不含自身成员参与计算过程之中。 #### 四、适用场景举例说明 以下是几个典型应用案例展示如何利用 Gap Lock 提升系统性能以及保障业务逻辑准确性: ##### 场景 A: 库存管理系统 在一个电商网站后台维护商品库存数量的过程中,为了避免多个订单同时扣减相同货物造成超卖风险,可以通过显式声明意向更新指令提前占据相应批次号段位,进而减少竞争冲突概率。 ```sql -- 占用编号介于100至200间的全部可用货品 LOCK TABLES products WRITE; START TRANSACTION; SELECT stock_quantity FROM products WHERE product_id >= 100 AND product_id < 200 FOR UPDATE; UPDATE products SET stock_quantity = stock_quantity - purchased_amount WHERE ... ; COMMIT; UNLOCK TABLES; ``` 此处采用 FOR UPDATE 关键字强制获取相关联区段上的写入许可权柄,间接激活了底层 GAP LOCK 控制策略. ##### 场景 B: 时间轴预约平台 设想构建一款在线日程安排工具允许用户自行挑选空闲时段预定会议室等活动场地。此时借助于高效能的关系型数据库解决方案能够轻松达成预期效果的同时兼顾用户体验流畅度需求. 假设有这样一张存储预订详情的基础表格 designates(id INT PRIMARY KEY AUTO_INCREMENT,start_time DATETIME NOT NULL,end_time DATETIME NOT NULL),我们可以编写类似下面这样的验证函数用来检测是否存在重叠时间段冲突状况发生 : ```sql CREATE FUNCTION check_overlap(new_start DATETIME,new_end DATETIME) RETURNS BOOLEAN DETERMINISTIC BEGIN RETURN EXISTS ( SELECT 1 FROM designates d WHERE new_start < d.end_time AND new_end > d.start_time); END$$ DELIMITER ; INSERT INTO designates(start_time ,end_time ) VALUES('2024-07-08 09:00','2024-07-08 11:00') AS NEW IF NOT(check_overlap('2024-07-08 09:00','2024-07-08 11:00')); ``` 这里巧妙运用到了基于时间戳字段建立起来的复合唯一性约束配合自定义辅助方法完成初步筛查过滤工作之后再进一步提交最终确认动作之前再次进行全面扫描排查任何可能出现的新旧事件交叉情形。由于涉及到连续数值比较运算所以必然伴随着相应的 GAPS LOCKING 操作介入进来保驾护航. ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值