10人面试9个答错?鹅厂T12详解MySQL加锁机制

news2024/12/22 18:21:38

00727c8ca56d7770be6d90576d995d13.png

a16c1d45ebd54a2c157fbf3ae85edb86.gif

👉腾小云导读

鹅厂有一道关于「数据库锁」的面试题。我们发现其实很多 DBA (数据库管理员,Database administrator)包括工作好几年的 DBA 都答得不太好。这说明 MySQL 锁的机制其实还是比较复杂,值得深入研究。本文对3条简单的查询语句加锁情况进行分析,以期帮助各位开发者彻底搞清楚加锁细节。欢迎阅读~

👉看目录,点收藏

1 MySQL 有哪些锁?

   1.1 全局锁

   1.2 表锁

   1.3 行锁

2 锁的兼容情况

3 锁信息查看方式

4 测试环境搭建

   4.1 建立测试表

   4.2 写入测试数据

5 记录存在时的加锁

6 记录不存在时的加锁

7 构造测试环境

8 主键范围读取

    8.1 RR 隔离级别

9 唯一索引等值查询

    9.1 RR 隔离级别

    9.2 RC 隔离级别

10 非唯一索引等值查询

11 非唯一索引加覆盖索引

12 无索引

13 总结

首先来看这个面试题:


已知表t是 innodb 引擘,有主键:id(int类型) ,下面3条语句是否加锁?加锁的话,是什么锁?

1. select * from t where id=X;

2. begin;select * from t where id=X;

3. begin;select * from t where id=X for update;

这里其实有很多依赖条件,并不能一开始就给出一个很确定的答复。我们一层层展开来分析。

01、MySQL 有哪些锁?

09a591b106244838e52f780dde940757.jpeg

首先要知道 MySQL 有哪些锁。如上图所示,至少有12类锁。

其中,自增锁是事务向包含了 AUTO_INCREMENT 列的表中新增数据时会持有, predicate locks for spatial index  为空间索引专用,本文不讨论这2类锁。

锁按粒度可分为全局、表级、行级,共3类。

1.1 全局锁

对整个数据库实例加锁。

加锁表现:数据库处于只读状态,阻塞对数据的所有 DML/DDL

加锁方式: Flush tables with read lock  

释放锁:unlock tables(发生异常时会自动释放)

作用场景:全局锁主要用于做数据库实例的逻辑备份,与设置数据库只读命令 set global readonly=true 相比,全局锁在发生异常时会自动释放

1.2 表锁

对操作的整张表加锁,锁定颗粒度大,资源消耗少。不会出现死锁,但会导致写入并发度低。具体又分为3类:

1)显式表锁

分为共享锁(S)和排他锁(X)

显式加锁方式:lock tables ... read/write

释放锁:unlock tables(连接中断也会自动释放)

2) Metadata-Lock (元数据锁)

MySQL5.5版本开始引入,主要功能是并发条件下,防止session1的查询事务未结束的情况下,session2对表结构进行修改,保护元数据的一致性。

在 session1 持有 metadata-lock 的情况下, session2 处于等待状态:show proces slist 可见 Waiting for table metadata lock 。其具体加锁机制如下:

  • DML->先加MDL 读锁( SHARED_READ,SHARED_WRITE )

  • DDL->先加MDL 写锁( EXCLUSIVE )

  • 读锁之间兼容

  • 读写锁之间、写锁之间互斥

3)Intention Locks(意向锁)

意向锁为表锁(表示为IS或者IX),由存储引擎自己维护,用户无法干预。

下面举一个例子说明其功能。假设有2个事务:T1和T2

T1: 锁住表中的一行,只能读不能写(行级读锁)。

T2: 申请整个表地写锁(表级写锁)。

如T2申请成功,则能任意修改表中的一行,但这与T1持有的行锁是冲突的。故数据库应识别这种冲突,让T2的锁申请被阻塞,直到T1释放行锁。

有2种方法可以实现冲突检测:

1、判断表是否已被其他事务用表锁锁住;

2、判断表中的每一行是否已被行锁锁住。

其中2需要遍历整个表,效率太低。因此 innodb 使用意向锁来解决这个问题:T1需要先申请表的意向共享锁(IS),成功后再申请某一行的记录锁S。

在意向锁存在的情况下,上面的判断可以改为:T2发现表上有意向共享锁IS,因此申请表地写锁被阻塞。

1.3 行锁

InnoDB 引擘支持行级别锁,行锁粒度小,并发度高,但加锁开销大,也可能会出现死锁。

加锁机制:innodb 行锁锁住的是索引页,回表时,主键地聚簇索引也会加上锁。


329b0ed70764383022c3c91cc139a1ff.png

行锁具体类别如上图所示,包括: Record lock/Gap Locks/Next-Key Locks ,每类又可分为共享锁(S)或者排它锁(X),一共2*3=6类,最后还有1类插入意向锁:

Record lock (记录锁):最简单的行锁,仅仅锁住一行。记录锁永远都是加在索引上的,即使一个表没有索引, InnoDB 也会隐式地创建一个索引,并使用这个索引实施记录锁。


Gap Locks (间隙锁):加在两个索引值之间的锁,或者加在第一个索引值之前,或最后一个索引值之后的间隙。使用间隙锁锁住的是一个区间,而不仅仅是这个区间中的每一条数据。间隙锁只阻止其他事务插入到间隙中,不阻止其他事务在同一个间隙上获得间隙锁,所以 gap x lock 和 gap s lock  有相同的作用。它是一个左开右开区间:如(1,3)。


Next-Key Locks :记录锁和间隙锁的组合,它指的是加在某条记录以及这条记录前面间隙上的锁。它是一个左开右闭区间:如(1,3】。


Insert Intention (插入意向锁):该锁只会出现在 insert 操作执行前(并不是所有insert操作都会出现),目的是提高并发插入能力。它在插入一行记录操作之前设置一种特殊的间隙锁,多个事务在相同的索引间隙插入时,如果不是插入间隙中相同的位置就不需要互相等待。

TIPS:

1.不存在 unlock tables … read/write ,只有 unlock tables 
2. If a session begins a transaction, an implicit UNLOCK TABLES is performed 

02、锁的兼容情况

引入意向锁后,表锁之间的兼容性情况如下表:

20600ea64cfc09306eeefda1471df292.png

总结:

意向锁之间都兼容。

X,IX和其它都不兼容(除了1)。

S,IS和其它都兼容(除了1,2)。

03、锁信息查看方式

MySQL 5.6.16版本之前,需要建立一张特殊表 innodb_lock_monitor ,然后使用  show engine innodb status  查看。

CREATE TABLE innodb_lock_monitor (a INT) ENGINE=INNODB;
DROP TABLE innodb_lock_monitor;

MySQL 5.6.16版本之后,修改系统参数 innodb_status_output 后,使用show engine innodb status 查看。

set GLOBAL innodb_status_output=ON;
set GLOBAL innodb_status_output_locks=ON;

每15秒输出一次INNODB运行状态信息到错误日志。

70a4533d60c87d1aac1c0ba9356e67ee.png

MySQL 5.7 版本之后

可以通过 information_schema.innodb_locks 查看事务的锁情况,但只能看到阻塞事务的锁;如果事务并未被阻塞,则在该表中看不到该事务的锁情况。

MySQL 8.0

删除 information_schema.innodb_locks ,添加

performance_schema.data_locks ,即使事务并未被阻塞,依然可以看到事务所持有的锁,同时通过 performance_schema.table_handles 、 performance_schema.metadata_locks 可以非常方便地看到元数据锁等表锁。

04、测试环境搭建

4.1 建立测试表

该表包含一个主键,一个唯一键和一个非唯一键:

CREATE TABLE `t` (
`id` int(11) NOT NULL,
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
`c` varchar(10),
PRIMARY KEY (`id`),
unique KEY `a` (`a`),
key b(b))
ENGINE=InnoDB;

4.2 写入测试数据

insert into t values(1,10,100,'a'),(3,30,300,'c'),(5,50,500,'e');

05、记录存在时的加锁

对于innodb引擘来说,加锁的2个决定因素:

一、当前的事务隔离级别。
二、当前记录是否存在。

假设 id 为3的记录存在,则在不同的4个隔离级别下3个语句的加锁情况汇总如下表( select  3表示  select * from t where id =3):

隔离级别select 2begin;select 2begin;select 2 for update
RUSHARED_READSHARED_WRITE

IX

X,REC_NOT_GAP:3

RCSHARED_READSHARED_WRITE

IX

X,REC_NOT_GAP:3

RRSHARED_READSHARED_WRITE
IX
X,REC_NOT_GAP:3
SerialSHARED_READ
IS
S,REC_NOT_GAP:3
SHARED_WRITE
IX
X,REC_NOT_GAP:3

分析:

使用以下语句在4种隔离级别之间切换:

set global transaction_isolation='READ-UNCOMMITTED';
set global transaction_isolation='READ-COMMITTED';
set global transaction_isolation='REPEATABLE-READ';
set global transaction_isolation='Serializable';

对于 auto commit=true , select  没有显式开启事务( begin )的语句,元数据锁和行锁都不加,是真的“读不加锁”。

对于 begin ; select ... where id =3这种只读事务,会加元数据锁SHARED_READ,防止事务执行期间表结构变化,查询performance_schema.metadata_locks 表可见此锁:


cd3cb5c7dcb0fe6ed19404dcf5255dad.png

对于 begin; select ... where id =3这种只读事务,MySQL在RC和RR隔离级别下,使用 MVCC 快照读,不加行锁,但在Serial隔离级别下,读写互斥,会加意向共享锁(表锁)和共享记录锁(行锁)。

对于begin; select ... where id=3 for update,会加元数据锁SHARED_WRITE。

对于begin; select ... where id=3 or update,4种隔离级别都会加意向排它锁(表锁)和排它记录锁(行锁),查询 performance_schema.data_locks 可见此2类锁。


55eb9e5be411ef91f91cd76cb7db6935.png

06、记录不存在时的加锁

隔离级别select 2begin;select 2begin;select 2 for update
RUSHARED_READSHARED_WRITE
IX
RCSHARED_READSHARED_WRITE
IX
RRSHARED_READSHARED_WRITE
IX
X,GAP:3
SerialSHARED_READ
IS
S,GAP:3
SHARED_WRITE
IX
X,GAP:3

分析:

当记录不存在的时候,RU和RC隔离级别只有意向锁,没有行锁了。

RR,Serial 隔离级别下,记录锁变成了 Gap Locks(间隙锁),可以防止幻读,lock_data 为3的 GAP lock 锁住区间(1,3),此时ID=2的记录插入会被阻塞。

e7a25ad6176837ce639de8d4d8f17a56.png


3c6fc9f50e28f587500f999f0b3e9aff.png

那么对于主键范围查询,唯一键查询,非唯一键查询,在不同隔离级别下又是如何加锁的呢?

07、构造测试环境

该表包含一个主键,一个唯一键和一个非唯一键,有3条测试记录:

CREATE TABLE `t` (
`id` int(11) NOT NULL,
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
`c` varchar(10),
PRIMARY KEY (`id`),
unique KEY `a` (`a`),
key b(b))
ENGINE=InnoDB;
insert into t values(1,10,100,'a'),(3,30,300,'c'),(5,50,500,'e');

08、主键范围读取

8.1 RR隔离级别

begin;
select * from t where id>1 and id<7 for update;

7037bc16680165ea5e821f0016246a39.png

b916bce7c002d599c1668b50c2848c4e.png

原则1:innodb 行锁锁住的是索引页。

原则2:索引查找过程中访问到的对象会加锁。

原则3:RR 隔离级别为了防止幻读,存在间隙锁(GAP LOCK)。

原则4:加锁的基本单位是 next-key lock,next-key lock 是前开后闭区间。

所以加了3个 X 锁(锁定记录本身和之前的区间,等于间隙锁+行锁),分别锁定(1,3】,(3,5】,(5,+∞】区间。

说明:

1. InnoDB 给每个索引加了一个不存在的最大值 supremum,代表+∞

2. 幻读:当某个事务在读取某个范围内的记录时,另一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻读。

09、唯一索引等值查询

9.1 RR隔离级别

begin;
select * from t where a=30 for update;

dc2eecbadc5a11344b8f96545603e659.png

原则1:innodb 行锁锁住的是索引页,回表时,主键地聚簇索引也会加上锁。

原则2:二级索引(非聚簇索引)的叶子节点包含了引用行的主键值。

原则3:索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁。

所以加了2个记录锁,记录锁30,3代表锁定唯一索引 a 上的(id=3,a=30)这条记录,记录锁3代表锁定了主键上的 id=3 这条记录。

9.2 RC隔离级别

begin;
select * from t where a=30 for update;

e49490533ba0a9cfe657fb865fc4ff1b.png

对于该条语句,RC 隔离级别下加锁完全一样。

10、非唯一索引等值查询

10.1 RR隔离级别

begin;
select * from t where b=300 for update;

75c509e866ce02d22a09b802e5efaa39.png

原则:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁。

所以对于非唯一索引 b,锁定了((b=100,id=1),(b=300,id=3)】区间和((b=300,id=3),(b=500,id=5))区间和主键上的 id=3。

begin;
select * from t where b=400 for update;

13a74d6d973f3ed30665583f408e6ff6.png

可以看到,查询得值 b=400不存在,但加锁情况和 b=300值存在的时候是一样的。

10.2 RC隔离级别

begin;
select * from t where b=300 for update;

a1d0ec3912081af0b20b59167b34b69a.png

原则:读提交隔离级别 (read-committed) 只有行锁,没有间隙锁。

所以只锁定了锁引 b 上的 (b=300,id=3) 和主键上的 id=3。

begin;
select * from t where b=400 for update;

57022f0a68aab1fe37b3b9d2a7410346.png

因为 RC 隔离级别没有间隙锁,所以 b=400 值不存在的时候,只有IX意向排它锁。

11、非唯一索引加覆盖索引

11.1 RR隔离级别

select id from t where b=300 lock in share mode;

f9e40800ec92e8b31df2cc0f204d690e.png

原则:如果一个索引包含所有需要查询的字段的值,就是覆盖索引,对于二级索引来说,可以避免对主键索引的查询(回表)。

因为二级索引 b 包括 (b,id),所以主键索引上无锁。

因为是 lock in share mode 所以加的是共享锁(S)和共享意向锁(IS)。

12、无索引

begin;
select * from t where c='aa' for update;

6cd4253a06afb85fb19793f228258beb.png

没有索引的时候,要全表扫描,有主键就扫主键。

所以锁定范围:(-∞,1]、(1,3]、(3,5]、(5,+supremum],可以看出来把整张表都锁住了,所以对于实时业务一定要避免非索引查询。

13、总结

以上就是我们腾讯的工程师对于 MySQL 加锁机制的见解,希望对你有帮助。有不一样看法的同学们也可以在公众号(点👉这里👈进入开发者社区,右边扫码即可进入公众号)评论区进行讨论噢。

eff8edf11b8438dfaf8bc1df557f3806.png

各位开发者还在面试中遇到过什么样的技术难题?可以在 公众号(点👉这里👈进入开发者社区,右边扫码即可进入公众号)评论区分享,一起讨论。我们将选取1则最有创意的分享,送出腾讯云开发者-鼠标垫1件(见下图)。5月25日中午12点开奖。

8429affbcea2621f7cf18dfa36c66889.jpeg

7777bee50fb56069e5582b63d67867f2.png

ec4f71a354e74d1673106862299a486f.png

750a86eb24f2de749fb344402368b799.png

932428ac44ba089d292984a8ccbc5657.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/542969.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

探索Vue的组件世界-实现Vue插件

一个好的框架满足几大设计原则&#xff1a; 开闭原则&#xff1a;对修改源码关闭&#xff0c;对功能扩展开放 vue作为一个优秀的组件框架&#xff1a;满足开闭原则&#xff0c;提供良好的插件机制&#xff0c;以提供三方来扩展功能 Mixin模式 Vue.mixin(mixin) 全局注册的m…

嵌入式 QT 定时器与计时器

目录 1、定时器 2、计时器 2.1 QTime 时间转换成字符串函数 3、QT 获取日期&#xff0c;时间&#xff0c;星期 4、综合应用 定时器是用来处理周期性事件的一种对象&#xff0c;类似于硬件定时器。例如设置一个定时器的定时周期为 1000 毫 秒&#xff0c;那么每 1000 毫秒就会…

现在的00后,真是卷死了呀,辞职信准备好了·····

都说00后躺平了&#xff0c;但是有一说一&#xff0c;该卷的还是卷。这不&#xff0c;三月份春招我们公司来了个00后&#xff0c;工作没两年&#xff0c;跳槽到我们公司起薪23K&#xff0c;都快接近我了。 后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了…

Kali-linux使用社会工程学工具包(SET)

社会工程学工具包&#xff08;SET&#xff09;是一个开源的、Python驱动的社会工程学渗透测试工具。这套工具包由David Kenned设计&#xff0c;而且已经成为业界部署实施社会工程学攻击的标准。SET利用人们的好奇心、信任、贪婪及一些愚蠢的错误&#xff0c;攻击人们自身存在的…

python使用海龟turtle实现绘制汉字、中文

一、实现要求 使用python中的turtle库绘制指定汉字、中文 二、实现思路 1、要想实现汉字的绘制&#xff0c;首先需要知道汉字的笔画坐标&#xff0c;汉字的笔画坐标在网上有&#xff0c;需要使用爬虫技术抓取到指定汉字的笔画坐标信息 2、根据汉字的笔画坐标信息&#xff0c;使…

基于Kubernetes的电商平台部署:实现高可用、弹性伸缩与容器化管理

▲ 点击上方"DevOps和k8s全栈技术"关注公众号 背景&#xff1a;电商平台的高可用性和可伸缩性是保证用户体验和业务发展的重要因素。Kubernetes&#xff08;K8s&#xff09;作为一个容器编排平台&#xff0c;可以提供强大的容器管理和自动化部署能力&#xff0c;使得…

人手一个 Midjourney,StableStudio 重磅开源!

公众号关注 “GitHubDaily” 设为 “星标”&#xff0c;每天带你逛 GitHub&#xff01; 上个月 19 号&#xff0c;Stability AI 开源大语言模型 StableLM&#xff0c;模型的 Alpha 版本有 30 亿和 70 亿参数&#xff0c;并支持商用。 过去仅一个月&#xff0c;Stability AI 再次…

鉴权管理系统(JWT技术架构)——SpringBoot2+Vue2(一定惊喜满满,万字长文)

初衷&#xff1a; 一直不太理解整个前后端的鉴权&#xff0c;跨域等问题&#xff0c;抽空两个晚上整理出万字文章&#xff0c;也是对于自己的一个交代&#xff0c;现在共享出来&#xff0c;希望大家也能受益&#xff0c;将使用过程在这里一一详述&#xff0c;还是多说一句&…

简述 JavaScript 中 prototype

简述 JavaScript 中 prototype 这篇笔记主要捋一下这么几个概念&#xff1a; JS 的继承构造函数new 的作用及简易实现__proto__ & prototype同样的方法&#xff0c;class 和 prototype 中分别是怎么实现的 基础概念 JS 是通过 prototype chaining 实现继承的语言&#…

linux(缓冲区学习)

目录&#xff1a; 1.对进程是如何和这个进程打开文件进行关联的总结 2.标准输出和标准错误都是往显示器上打印--有何区别 3.缓冲区 --------------------------------------------------------------------------------------------------------------------------- 1.对进程是…

双模齐下,提质增效:知微携手CODING共创BizDevOps体系新篇章

为了提升工作和管理效率&#xff0c;工具建设是许多企业不得不面对的现实&#xff0c;然而在工具建设落地过程中&#xff0c;往往存在一系列的问题。如不同组织、部门之间互不相通&#xff0c;各自为政&#xff0c;工具流程与实际工作所需不符&#xff0c;导致工具建设的结果是…

做实大模型的产业价值,度小满深耕“NLP+金融”

2023年的五月&#xff0c;称得上一句AI之夏。 大模型层出不穷、扎堆发布。 这一轮由大模型推动的AI热潮中&#xff0c; NLP&#xff08;自然语言处理&#xff09;技术与金融落地场景的结合备受期待。金融行业是数字化、智能化的先行者&#xff0c;也是大模型技术落地的最佳领域…

chatgpt赋能Python-python3h怎么操作

Python3 SEO操作指南 Python3语言已成为计算机编程领域的标准和主要工具之一。SEO&#xff08;Search Engine Optimization&#xff09;是一种促进网站在搜索引擎结果中排名的技术。Python3也可以用来执行SEO操作&#xff0c;本文将介绍如何使用Python3进行SEO操作&#xff0c…

替换字符串的关键字KeywordProcessor

【小白从小学Python、C、Java】 【等级考试500强双证书考研】 【Python-数据分析】 替换字符串的关键字 KeywordProcessor [太阳]选择题 以下说法错误的一项是&#xff1a; from flashtext import KeywordProcessor myKP KeywordProcessor() myKP.add_keyword(English, Math) …

别在碳排放问题上大搞双重标准!

* * * 原创&#xff1a;刘教链 * * * 隔夜比特币从26.5k一线奋力跃升&#xff0c;回升至27.5k一线。 最近美国有些人又要对比特币的碳排放搞双重标准了。说的是比特币挖矿烧掉了多少多少电力&#xff0c;折合多少多少碳排放&#xff0c;因此应当加征多少多少排放税&#xff0c…

springboot+java电影院售票订票选座推荐系统554c6

主页是注册&#xff0c;登录&#xff0c;搜索。 用户在注册之前可以进行搜索查询现在上映的和即将上映的影片信息&#xff0c;但是不能在线购票。购票需注册登录之后方可。 用户可以修改自己注册后的账户信息&#xff0c;注册成功后直接登录。退出网页后&#xff0c;取消登录信…

ChatGPT APP来了,还可以直接订阅Plus账号,操作流程都这篇里面

大家好&#xff0c;我是可夫小子&#xff0c;关注AIGC、读书和自媒体。解锁更多ChatGPT、AI绘画玩法。加&#xff1a;keeepdance&#xff0c;备注&#xff1a;chatgpt&#xff0c;拉你进群。 OpenAI宣布推出聊天机器人ChatGPT的APP&#xff0c;目前已上架苹果App Store&#xf…

三十八、流控效果、热点数据限流、熔断降级、push到配置中心nacos

1、流控效果 流控效果是指请求达到流控阈值时应该采取的措施&#xff0c;包括三种&#xff1a; 快速失败&#xff1a;达到阈值后&#xff0c;新的请求会被立即拒绝并抛出FlowException异常。是默认的处理方式。 warm up&#xff1a;预热模式&#xff0c;对超出阈值的请求同样是…

OpenAI的巨额捐款背后,马斯克到底捐了多少?

来源&#xff1a;Techcrunch 作者&#xff1a;Mark Harris 编译&#xff1a;巴比特 自 2018 年 2 月退出 OpenAI 董事会以来&#xff0c;埃隆马斯克&#xff08;Elon Musk&#xff09;一直对 OpenAI 深感失望&#xff0c;这已不是什么秘密&#xff0c;最终他在一封公开信中呼吁…

每日涨停个股增量加入股票池,持续跟踪走势!股票量化分析工具QTYX-V2.6.5

功能概述 目前A股市场的股票每天是有限制最大涨幅的&#xff0c;也就是涨停的概念。比如主板个股最大涨幅是10%&#xff0c;创业板个股最大涨幅是20%等。 对于个股而言并不是随随便便就能被推到涨停板的。或是因为股票发生了重大的利好&#xff08;资产重组、政策利好、业绩暴增…