MySQL 啥时候用记录锁,啥时候用间隙锁?

 MySQL 啥时候会用记录锁,啥时候会用间隙锁,啥时候又会用 Next-Key 锁呢?今天我们就来做一些测试,弄清楚这个问题。

文章思维导图

影响因素

在开始之前,我们需要声明的是:本文所有测试及结论的前提均是在「可重复读」隔离级别下,以及 Innodb 存储疫情下。

根据网上资料,我们大概可以知道,影响其使用哪种行级锁的因素有:

  1. 索引类型(聚簇索引、唯一二级索引、普通二级索引)

  2. 匹配类型(精确匹配、唯一匹配、范围匹配)

  3. 事务隔离级别

  4. 是否开启 Innodb_locks_unsafe_for_binlog 系统变量

  5. 记录是否被标记删除

  6. 具体的执行语句类型(SELECT、INSERT、DELETE、UPDATE)

为了让文章相对易懂一些,我准备重点测试索引类型与匹配类型两个影响因素。对于其他的影响因素,我将不做改动。例如:事务隔离级别固定为「可重复读」,Innodb_locks_unsafe_for_binlog 固定为 false。而第 5、6 点相对来说简单一些,则我们会简单带过。

针对上面几个影响因素,我们指定了几个测试实验,分别是:

  1. 聚簇索引 + 精确匹配

  2. 聚簇索引 + 范围匹配

  3. 唯一二级索引 + 精确匹配

  4. 唯一二级索引 + 范围匹配

  5. 普通二级索引 + 精确匹配

  6. 普通二级索引 + 范围匹配

// 表结构
CREATE TABLE `test`.`price_test` (
  `id` BIGINT(64) NOT NULL AUTO_INCREMENT,
  `price` INT(4) NULL,
  PRIMARY KEY (`id`));
// 表中数据
1, apple, 10
2, orange, 30
50, perl, 60

聚簇索引 + 精确匹配

为了测试「聚簇索引 + 精确匹配」下加锁的类型,我们采用如下的测试方法。

事务 A 执行下面命令:

begin;
select * from price_test where id = 2 for update;

执行 show engine innodb status\G; 查看锁信息如下图所示。

可以看到,其是对 id 为 2 的索引加了一个记录锁。

此时事务 B 执行下面命令:

beign;
update price_test set price = 25 w
### MySQL记录锁间隙锁的概念 #### 记录锁 (Record Lock) 记录锁是最常见的行级锁,用于锁定特定的索引记录。当事务对某个具体的数据行执行更新或删除操作时会自动加上记录锁。 - **作用范围**: 锁定的是具体的索引条目。 - **应用场景**: 当需要确保同一时间只有一个事务能够修改某一条特定记录时使用此类型的锁。 例如,在`SELECT ... FOR UPDATE`语句中会对查询到的具体记录加排他锁[^3]: ```sql BEGIN; SELECT * FROM accounts WHERE id = 123 FOR UPDATE; UPDATE accounts SET balance = balance - 100 WHERE id = 123; COMMIT; ``` 这段代码展示了如何通过显式的`FOR UPDATE`来获取记录上的排他锁,防止其他事务在此期间对该账户余额做出更改。 #### 间隙锁 (Gap Lock) 间隙锁并不是针对实际存在的数据行而是存在于两个相邻索引项之间、第一个索引之前以及最后一个索引之后的位置上。这种锁主要用于阻止新记录被插入到指定范围内从而避免发生幻读现象。 - **作用范围**: 不涉及任何真实的数据行;仅覆盖由现有索引定义出来的空白区域。 - **应用场景**: 主要是为了预防幻影问题而设计的一种机制,即在同一条件下再次运行相同的查询可能会返回不同的结果集大小。 下面的例子说明了怎样利用间隙锁保护一段连续编号的空间不被外部干扰: 假设有一个订单表orders按照order_id排序存储着客户提交的商品订购请求,并且现在想要批量处理一批新的订单但是又不想让别人在这段时间内插队,则可以通过如下方式实现: ```sql START TRANSACTION; -- 假设当前最大 order_id 是999, 新增订单将从1000开始 INSERT INTO orders VALUES (NULL,'pending', NOW()) /* 这里省略了一些字段 */; SET @last_order_id := LAST_INSERT_ID(); -- 对即将使用的ID段施加间隔控制 SELECT MAX(order_id)+1 AS next_val FROM orders WHERE order_id < @last_order_id LOCK IN SHARE MODE; /* 执行业务逻辑 */ ... COMMIT; ``` 在这个例子里面,虽然并没有直接指明哪个确切位置应该受到保护,但确实有效地封锁住了可能影响后续流程的那个潜在空间内的所有可能性变化。 ### 区别与联系 - 记录锁关注于单个已知实体对象本身的安全性保障; - 而间隙锁则更侧重于维护某些未占用资源间的相对稳定性。 - **共同点** - 都属于InnoDB引擎内部为了提高并发性能并保持ACID属性所采取的重要措施之一; - 实际应用当中往往两者配合起来一起工作形成所谓的Next-Key Lock(临键锁),既包含了对于具体项目的独占权限同时也兼顾到了周围环境的影响因素考虑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值