MySQL间隙锁

本文详细解析了InnoDB的间隙锁机制,指出网上关于间隙锁的一些常见错误理解,特别是关于左闭右开和左开右闭的概念。通过具体SQL示例和索引结构图,说明了间隙锁在二级索引中的应用,强调在主键和唯一性索引条件下不存在间隙锁。作者纠正了插入数据的误解,并阐述了间隙锁的真正范围,即条件查询命中记录前后两条记录之间的间隙。结论指出,只有插入该范围内的数据才会被阻塞,否则不会被锁定。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        今天,在公司每周的技术分享会上一个入行不到一年的同事分享了下InnoDB的加锁机制,里面提到了间隙锁,内容和举的用例截图都是简书(当然简书上的内容也是Ctrl+C/V别的博客的)上的,估计博主自己也没有验证就发上去了(因为内容是错的<也可能是描述过于深奥,像我们这种普通人会理解错>,当然有可能5.7<不含5.7>以前的版本是他们说的这样吧,我没去试)

        网上说的什么左闭右开还是什么左开右闭(记不清了,反正说法肯定是这两种的一种)都是的。而且有的举例竟然拿主键做条件去举例,主键和唯一性索引作为条件是没有间隙锁的(存在的情况下, 否则还是会锁住间隙)。

        先上sql

        

         

         id为主键,create_id为一个二级索引

        上面数据在create_id这个索引树中存储的结构是下图这样的

        

        所以这时候我们如果执行了

begin;
select * from test_pk where create_id = 7 for update;

         的时候,只有数据被插入到下图两个圈圈范围内的数据才会被阻塞,比如create_id = 10 and id = 1(这条记录会插在10-8和7-6的中间)的数据和create_id = 5 and id = 9(这条数据会插在5-2和7-6的中间)的数据就会被阻塞,而create_id = 10 and id = 9(这条数据会插在10-8这条记录的后面)的数据和create_id =5 and id = 1(这条记录会插在5-2的前面)的数据就可以顺利写入。如下面的简化图(下图中c_id为create_id简写):

         

   

         有的人可能会问,为什么一定要这么插入?不能插入到其他地方?这个问题可以去看一下InnoDB索引还有B树B+树相关的内容,看完就明白啦~

        所以间隙锁的范围其实就是条件中二级索引的索引树上最贴近实际查询命中记录的两条记录(前一条和后一条)之间的范围,只要插入的数据不在这个范围内就不会阻塞。这个结论同样适用于没有命中的条件,比如上述test_pk表中没有create_id=6这条记录,对于select * from test_pk where create_id = 6 for update;这条语句,间隙锁范围是5-2到7-6之间,并不是有的博客说的膨胀为表锁。

### 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 操作介入进来保驾护航. ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值