mysql数据库 行级锁,间隙锁和临键锁详解

目录

准备

查看锁命令

演示

普通的select语句

共享锁与排他锁

无索引行锁升级为表锁

间隙锁&临键锁

索引上的等值查询(索引为唯一索引)

索引上的等值查询(索引为普通索引)

索引上的范围查询(唯一索引)


准备

我的mysql版本是8。

CREATE TABLE `user` (
  `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `name` varchar(100) DEFAULT NULL COMMENT '姓名',
  `age` tinyint unsigned DEFAULT NULL COMMENT '年龄',
  `gender` tinyint unsigned DEFAULT NULL COMMENT '性别, 1:男, 2:女',
  `phone` varchar(11) DEFAULT NULL COMMENT '手机号',
  PRIMARY KEY (`id`),
  KEY `id_age` (`age`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表';

演示锁的时候,就通过上面这张表来演示一下。

查看锁命令

可以通过以下SQL,查看意向锁及行锁的加锁情况:

select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_locks;

常见的 lock_mode 值有:

  1. IS (Intent Shared Lock):意图共享锁。表示事务打算获取共享锁,但当前只在上级资源(如表)上锁定,而不是具体的行或页。

  2. IX (Intent Exclusive Lock):意图排他锁。表示事务打算获取排他锁,但当前只在上级资源(如表)上锁定。

  3. S (Shared Lock):共享锁。多个事务可以同时持有共享锁,允许读取资源,但禁止修改。

  4. X (Exclusive Lock):排他锁。一个事务独占此锁,可以读取和修改资源,其他事务无法访问该资源。

  5. SIX (Shared Intent Exclusive Lock):共享意图排他锁。表示事务允许共享访问某些资源,同时声明可能会对部分资源进行排他操作。

  6. SRX (Shared Read Exclusive Lock):共享读排他锁。类似于共享锁,同时表示将对某些资源进行排他访问。

  7. S+ (Next-key Shared Lock):下一键共享锁,InnoDB的特殊锁模式,用于实现间隙锁机制,防止幻读。

  8. X+ (Next-key Exclusive Lock):下一键排他锁,InnoDB的特殊锁模式,用于锁定间隙及其后的索引键,适用于更新或删除操作。

  9. AUTO-INC (Auto-Increment Lock):自增锁,专门用于处理AUTO_INCREMENT列,确保插入操作的原子性和顺序。

演示

普通的select语句

普通的select语句,执行时,不会加锁。

共享锁与排他锁

(1)select...lock in share mode,加共享锁,

共享锁与共享锁之间兼容。 

共享锁与排他锁之间互斥。

(2)update语句加排他锁

排他锁与排他锁之间互斥。

当客户端一执行update语句,会为id为1的记录加排他锁;

客户端二如果也执行update语句,更新id为1的数据,也要为id为1的数据加排他锁,但是客户端二会处于阻塞状态,因为排他锁之间是互斥的。

直到客户端一把事务提交了,才会把这一行的行锁释放,此时客户端二解除阻塞。

无索引行锁升级为表锁

在两个客户端中执行如下操作:

在客户端一中开启事务,并执行update语句,更新name为白眉鹰王的数据,也就是id为1的记录。

然后在客户端二中更新iid为1和d为3的记录,却不能直接执行,会处于阻塞状态,为什么呢?

原因就是此时客户端一根据name字段进行更新时,name字段是没有索引的,如果没有索引,此时行锁会升级为表锁(因为行锁是对索引项加的锁,而name没有索引)。

接下来,我们再针对建立索引但的字段age,索引建立之后,再次做一个测试:

此时我们可以看到,客户端一开启事务,根据age进行更新。而客户端二在更新id为3的数据时,更新成功,并未进入阻塞状态。

这样就说明,我们根据索引字段进行更新操作,就可以避免行锁升级为表锁的情况。

间隙锁&临键锁

索引上的等值查询(索引为唯一索引)

(1)当对唯一索引上进行等值查询时,给存在的记录加锁时, 是记录锁。

这意味着锁定的对象是索引记录本身,而不是记录之间的间隙。REC_NOT_GAP 锁用于精确锁定某个索引记录,以防止其他事务对该记录的修改或删除,但不影响索引记录之间的空隙。这种锁常用于阻止其他事务更新或删除当前事务所锁定的索引记录,同时允许其他事务插入新的记录到该索引记录之间的间隙。 

(2)当对唯一索引上进行等值查询时,给不存在的记录加锁时, 优化为间隙锁。

X,GAP 表示当前事务在一个索引的间隙上加了一个排他锁。这意味着在当前事务持有该锁期间,其他事务不能在该间隙中插入新记录。这种锁定机制通常用于防止并发事务产生幻读问题,从而确保事务隔离级别为 REPEATABLE READ 或 SERIALIZABLE

索引上的等值查询(索引为普通索引)

当非唯一普通索引进行等值查询时,向右遍历时最后一个值不满足查询需求时,next-key lock 退化为间隙锁。

分析一下,我们知道InnoDB的B+树索引,叶子节点是有序的双向链表。

假如我们要根据这个二级索引查询值为18的数据,并加上共享锁,我们是只锁定18这一行就可以了吗?

并不是,因为是非唯一索引,这个结构中可能有多个18的存在,所以,在加锁时会继续往后找,找到一个不满足条件的值(当前案例中也就是29)。此时会对18加临键锁,并对29之前的间隙加锁。

(1)如果等值查询的记录存在,

(2)如果等值查询的记录不存在, 

索引上的范围查询(唯一索引)

当对唯一索引上进行范围查询时,会访问到不满足条件的第一个值为止。

(1)查询的条件为id>=10,并添加共享锁。 此时我们可以根据数据库表中现有的数据,将数据分为4个部分:

  • [10]
  • (10,16]
  • (16,20]
  • (20,+∞] 

所以数据库数据在加锁时,就是将10加了行锁,16的临键锁(包含16及16之前的间隙),20的临键锁(包含20及20之前的间隙),正无穷的临键锁(正无穷及之前的间隙)。

(1)如果查询的条件为id>=11,并添加共享锁。 此时我们可以根据数据库表中现有的数据,由于id=11不存在,因此将数据分为3个部分:

  • (10,16]
  • (16,20]
  • (20,+∞] 

所以数据库数据在加锁时,就是加了16的临键锁(包含16及16之前的间隙),20的临键锁(包含20及20之前的间隙),正无穷的临键锁(正无穷及之前的间隙)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值