【MySQL】说透锁机制(一)行锁 加锁规则 之 等值查询,Golang面试题选择题

在这里插入图片描述

  • Next-key Lock:Record Lock + Gap Lock 的组合,既锁 索引记录 又锁 间隙,很多地方都称它是临键锁邻键锁,但我觉得直接翻译成下一个键锁会更好理解,意思是锁由“下一个键负责”,原则:左开右闭 或称 前开后闭
    上面的例子的区间为(圆括号表示不包括区间点,方括号表示包括区间点):

(下界限, 10]
(10, 20]
(20, 30]
(30, 40]
(40, 上界限supremun)

当给索引值20加上了Next-key Lock,那么这个范围是 (10,20] 包括20 ,而不包括10。

由于上界限supremun实际是个伪值,所以上界限并不是真正的索引记录。因此,实际上,这个Next-key Lock只锁定最大索引值之后的间隙。


三、加锁规则 之 等值查询

明白了3种算法,那么这3种算法又是怎么落地的呢?
实际上,默认使用的是Next-key Lock,也就是 索引记录 和 间隙 全锁上。但也会在不同场景下降级优化为Gap Lock或Record Lock。那我们就来分析一下:
由于在读已提交(RC)事务隔离级别下,间隙锁是禁用的(官方说是仅用于外键约束检查和重复键检查),这不是重点,所以本文主要深入分析:在默认的可重复读(RR)事务隔离级别下的加锁规则 之 等值查询

等值查询也就是where条件: = ,因为行锁都是对索引上锁,所以我们主要分析InnoDB引擎常见的3类索引:

  • 聚集索引(主键:简称pk
  • 唯一索引(简称uk
  • 普通索引(简称idx

分析数据准备

准备一个ct(country team 国家队)表:id 是自增主键,abc是普通索引,abc_uk是唯一索引
并插入4条初始数据:

CREATE TABLE ct (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
name varchar(20) NOT NULL,
abc int(10) unsigned NOT NULL,
abc\_uk int(10) unsigned NOT NULL,
remark varchar(100) DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk\_abc\_uk (abc\_uk) USING BTREE,
KEY idx\_abc (abc)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
INSERT INTO ct
(id, name, abc, abc\_uk, remark)
VALUES
(10, ‘巴西’, 10, 10, NULL),
(20, ‘阿根廷’, 20, 20, NULL),
(30, ‘葡萄牙’, 30, 30, NULL),
(40, ‘法国’, 40, 40, NULL);

预览下数据:

mysql> select * from ct;
±—±-------±----±-------±-------+
| id | name | abc | abc_uk | remark |
±—±-------±----±-------±-------+
| 10 | 巴西 | 10 | 10 | NULL |
| 20 | 阿根廷 | 20 | 20 | NULL |
| 30 | 葡萄牙 | 30 | 30 | NULL |
| 40 | 法国 | 40 | 40 | NULL |
±—±-------±----±-------±-------+
4 rows in set (0.00 sec)

我们新建Session1,做以下基本设置:

  • 先确认是:可重复读(RR)事务隔离级别

mysql> select @@tx_isolation;
±----------------+
| @@tx_isolation |
±----------------+
| REPEATABLE-READ |
±----------------+

  • 如果不是,需要在各Session中设置一下:

set tx_isolation=‘repeatable-read’;

  • Session1中开启锁的监视器:

SET GLOBAL innodb_status_output=ON;
SET GLOBAL innodb_status_output_locks=ON;

  • 查询是否开启:

mysql> show variables like ‘%innodb_status_output%’;
±---------------------------±------+
| Variable_name | Value |
±---------------------------±------+
| innodb_status_output | OFF |
| innodb_status_output_locks | OFF |
±---------------------------±------+

我操作的步骤,如下图:
在这里插入图片描述

这个Session1就留着我们分析锁来用,具体执行SQL我们新开另一个Session2,好了,准备开始~

3.1 聚集索引

我们先从聚集索引开始说起,那么这里也分等值条件有匹配无匹配索引两种情况,对应上的锁也是不同的,让我们来分别瞧一瞧:

有匹配索引

Session2执行SQL如下(按id=10):

begin;
update ct set remark = ‘怀念2002年, 巴西夺冠, 中国进世界杯’
where id = 10;

注意不要commit或rollback,以便于我们分析行锁

然后我们在"Session1"查看锁的详细信息

show engine innodb status\G;

我们主要看TRANSACTIONS这段,如下图:
在这里插入图片描述
我们来分析一下,上图中包含的信息:

  1. 1 row lock(s)就代表上了1个行锁(不要理解成只锁了1行🐼);
  2. 具体的行锁信息从RECORD LOCKS开始:
    每个RECORD LOCKS都会标明上锁的索引,就是index后面的,当前是PRIMARY,即代表上锁的索引是聚集索引;
    可能有多条RECORD LOCKS(当前只有一条);
  3. RECORD LOCKS下面紧跟着是它所有的Record lock记录:
    每条Record lock下面是具体的索引物理记录,第0个就是索引记录的key:当前hex 0000000a是指十六制的10,所以可以得知这个行锁 锁的是id=10的聚集索引记录
    我们以第0个来识别是哪个索引key就可以了,下面的1~6是索引记录上携带的数据,聚集索引保存了所有字段信息,所以比较多,其它索引只有2行:索引值和聚集索引的值;
    另外,Record Lock也可能有多条,这里只上了1个行锁,所以只有一条Record lock, heap no。。。

小结:

等值查询 匹配到 聚集索引 时,行级锁 会上一把 无间隙的Record Lock
这里是因为聚集索引id具有唯一性,所以Next-key Lock降级优化为Record Lock。

无匹配索引

先在Session2 rollback上一个SQL,再执行SQL如下(按id=11 不存在):

begin
update ct set remark = ‘没有id=11的记录~~’
where id = 11;

注意不要commit或rollback,以便于我们分析行锁

然后我们在"Session1"查看锁的详细信息

show engine innodb status\G;

我们主要看TRANSACTIONS这段,如下图:
在这里插入图片描述
小结:

等值查询 未匹配到 聚集索引 时,行级锁 会上一把 间隙锁

为什么是对 id=20 加的锁,而不是对 id=11 加的锁呢?

我们来分析一下:

  1. 行锁都是对索引记录加锁(除了伪值上界限supremun),因为id=11的索引不存在,所以无法对id=11加锁。
  2. 索引都是排好序的,按顺序从左向右扫描,直到找到 id=20 时,才可以确定 id=11 不存在,也就是说id=20 是 id =11 的next key,所以是对id=20的索引加锁,这里不是Next-key Lock而是间隙锁我觉得也是合理的,毕竟只锁间隙就可以了,范围是(10,20),不包括20。

按这么说,可能有同学又有疑问:如果id大于最大索引值,锁哪个索引记录?
咱们直接看结果,锁的伪值:上界限supremum,范围是(40, supremum),不包括40.

update ct set remark = ‘比最大id还要大!’
where id = 41;

在这里插入图片描述

3.2 唯一索引

有匹配索引

先在Session2 rollback上一个SQL,再执行SQL如下(按abc_uk=10):

begin;
update ct set remark = ‘怀念2002年, 巴西夺冠, 中国进世界杯’
where abc_uk = 10;

注意不要commit或rollback,以便于我们分析行锁

然后我们在"Session1"里查看锁的详细信息

show engine innodb status\G;

我们主要看TRANSACTIONS这段,如下图:
在这里插入图片描述
和聚集索引非常类似,不做赘述,但这里是上了2个行锁,所以有两条Record lock, heapno。。。

小结:

等值查询 匹配到 唯一索引 时,行级锁上了2把锁:

  1. 锁了一条唯一索引记录(abc_uk=10)
  2. 锁了一条聚集索引记录(id=10)

因为唯一索引具有唯一性,所以都是无间隙的Record Lock,这里也是Next-key Lock降级优化为Record Lock。

无匹配索引

先在Session2 rollback上一个SQL,再执行SQL如下(按abc_uk=35):

begin
update ct set remark = ‘没有abc_uk=35的记录~~’
where abc_uk = 35;

注意不要commit或rollback,以便于我们分析行锁

然后我们在"Session1"查看锁的详细信息

show engine innodb status\G;

我们主要看TRANSACTIONS这段,如下图:
在这里插入图片描述
小结:

等值查询 未匹配到 唯一索引 时,行级锁 会上一把 间隙锁,与聚集索引规则相同,具体不做赘述。

3.3 普通索引

有匹配索引

先在Session2 rollback上一个SQL,再执行SQL如下(按abc=10):

begin;
update ct set remark = ‘怀念2002年, 巴西夺冠, 中国进世界杯’
where abc = 10;

注意不要commit或rollback,以便于我们分析行锁

然后我们在Session1里查看锁的详细信息

show engine innodb status\G;

我们主要看TRANSACTIONS这段,如下图:
在这里插入图片描述
我们来分析一下:
这里就有意思了,上了3个行锁,还是3种不同的行锁,3种算法都齐了,咱们统一说一下怎么区分:

  1. RECORD LOCKS后面带locks rec but not gap:这说明是无间隙的Record Lock
  2. RECORD LOCKS后面带locks gap before rec:这说明是间隙锁Gap Lock
  3. RECORD LOCKS后面不带1和2的,就说明是默认的Next-key Lock

小结:

等值查询 匹配到 普通索引 时,行级锁上了3把锁:

  1. abc=10的普通索引记录上了Next-key Lock,这里的范围是:(下界值, 10]
  2. id=10的聚集索引记录上了Record Lock(单条)
  3. abc=20的普通索引记录上了Gap-key Lock,这里的范围是:(10, 20)

可以这样说:一个普通索引的等值查询update时,相当于把这条索引记录前后的空隙都锁上了~

这和聚集索引、唯一索引有着很大的不同,你知道这是为什么吗?
思考一下!!!
我们新开一个Session3先来验证一下吧:
在这里插入图片描述

特殊说明:
正常的锁超时异常是:ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
^C – query aborted 这是我不想等锁超时按Ctrl+C中止了🐼

验证第1把Next-key Lock

  • 插入abc=1和9的索引记录会阻塞,直至超时异常

INSERT INTO ct(id, name, abc, abc\_uk, remark)
VALUES (11, ‘英国’, 1, 21, NULL);

INSERT INTO ct(id, name, abc, abc\_uk, remark)
VALUES (21, ‘英国’, 9, 11, NULL);

  • 更新abc=10的索引记录会阻塞,直至超时异常

update ct set remark = ‘怀念2002年, 巴西夺冠, 中国进世界杯’
where abc = 10;

验证第2把Record Lock

  • 更新id=10的索引记录会阻塞,直至超时异常

update ct set remark = ‘怀念2002年, 巴西夺冠, 中国进世界杯’
where id = 10;

验证第3把Gap Lock

  • 插入abc=11和19的记录会阻塞,直至超时异常

INSERT INTO ct(id, name, abc, abc\_uk, remark)
VALUES (1, ‘英国’, 11, 21, NULL);

INSERT INTO ct(id, name, abc, abc\_uk, remark)
VALUES (21, ‘英国’, 19, 1, NULL);

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Go语言工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Go语言全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Golang知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Go)
img

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

]
[外链图片转存中…(img-pty1Q6pW-1713000338326)]
[外链图片转存中…(img-biaIr17r-1713000338327)]
[外链图片转存中…(img-15euVp61-1713000338328)]
[外链图片转存中…(img-OWF7bybz-1713000338328)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Golang知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Go)
[外链图片转存中…(img-RhsAaZJz-1713000338329)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 18
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值