【MySQL】MySQL中的锁

news2024/11/24 17:17:07

全局锁

全局锁是对整个数据库实例加锁,整个库处于只读状态。

 flush tables with read lock 

适用场景

全局锁适用于做全库逻辑备份,但是整个库处于只读状态,在备份期间,所有的更新操作、DDL将会被阻塞,会对业务产生影响。

single-transaction

mysqldump备份时可以使用–single-transaction参数,在备份数据之前启动一个事务,借助于MVCC获取到一致性视图,保证在备份的过程中,还支持数据的更新操作。

但是single-transaction只能用于支持事务的引擎,比如MyISAM不支持事务,所以使用MyISAM引擎的时候,是无法使用single-transaction的。

表级锁

表级锁分为表锁和元数据锁。

表锁

表锁从名字上就可以看出锁的是数据库表(Table),语法为:

# 锁住某张表
lock tables 表名 read/write
# 释放锁
unlock tables 表名

因为表锁的粒度太大,将整张表锁住,所以一般不使用表锁。

元数据锁MDL

元数据锁(meta data lock)不需要显示的使用,访问表的时候会自动添加MDL锁,添加MDL锁的原因是防止表结构出现不一致,假设查询数据的过程中,突然表结构被修改了,与最开始拿到的表结构不一致,在某些场景下可能会影响非常大。

MDL读锁

在对表做增删改查的时候,添加的是MDL读锁。

为什么添加的是读锁?

因为读锁之间不互斥,可以保证多个线程同时对一张表进行增删改查。

MDL写锁

在对表结构做修改的时候,添加的是MDL写锁。

为什么是写锁?

因为写锁与读锁之间相互互斥,当然写锁和写锁之间更是互斥的,既然要保证数据修改的安全性,那么如果有读操作在进行,是不能进行表结构变更操作的,反之亦是如此,如果正在修改表结构,也是不能进行读操作的,必须要等待前一个操作完成才可以进行下一个操作。所以使用了写锁,通过互斥保证数据操作的安全性。

**需要注意的是在事务中添加MDL锁的时候,直到整个事务提交后才会释放锁,如果此时遇到长事务,就会一直占用锁。**如果在这种情况下需要修改表结构,可以通过以下两种方式:

  1. 通过innodb_trx查询事务的trx_mysql_thread_id,将事务kill掉:

    mysql> SELECT trx_id, trx_state, trx_started, trx_mysql_thread_id,trx_autocommit_non_locking  FROM  information_schema.innodb_trx; 
    +-----------------+-----------+---------------------+---------------------+----------------------------+
    | trx_id          | trx_state | trx_started         | trx_mysql_thread_id | trx_autocommit_non_locking |
    +-----------------+-----------+---------------------+---------------------+----------------------------+
    | 422151119956664 | RUNNING   | 2021-07-02 23:27:06 |                   5 |                          0 |
    +-----------------+-----------+---------------------+---------------------+----------------------------+
    1 row in set (0.00 sec)
    

    kill事务命令:kill 事务线程ID(trx_mysql_thread_id)

    mysql> kill 5;
    Query OK, 0 rows affected (0.00 sec)
    
  2. 如果请求很频繁,可能刚kill掉就有新的事务到来,这个时候可以在修改表结构的时指定等待时间来获取MDL锁,如果在等待时间内都没有拿到锁,就先放弃,之后在合适的时间再修改表结构。

行锁

行锁锁住的是数据库表的行记录,但不是所有的引擎都支持行锁,比如MyISAM就不支持,所以对于MyISAM只能使用表锁。

如果某个字段存在索引,那么以该字段为查询条件时添加的行锁只需要锁住满足条件的数据行即可,如果不存在索引,MySQL需要全表扫描查找数据,此时会锁住所有的行,也就是退化为了表锁。

两阶段锁协议

在InnoDB事务中,行锁在需要的时候才加上,比如开始执行一个UPDATE语句,但是并不是UPDATE语句结束之后锁就释放了,而是在事务结束之后才释放,所以在实际开发中,可以将容易引起锁冲突的操作尽量往后放,减少锁的时间。

间隙锁Gap Lock

MySQL默认的隔离级别为可重复读,以下情况没有特殊说明,默认都是在可重复读隔离级别下。

当前读和快照读
在看间隙锁之前先看下MySQL的当前读和快照读。

快照读
MySQL的MVCC机制,在每个事务开启时会为其生成一个一致性视图,以此实现读提交/可重复读隔离级别,快照读指的就是从这个生成的一致性视图读取数据。
关于MVCC机制可参考:【MySQL】MVVC机制

当前读
当前读指的是读取undo log版本链中最新的记录,也就是读取最新的数据(已经提交的),如果是更新(update/insert/delete)操作都是当前读,select在可重复读的隔离级别下是快照读,不过可以使用以下语句使其变成当前读:

select xx from xx lock in share;
select xx from xx for update;

lock in share mode会加读锁,for update会加写锁,这两种语句都会进行当前读。

间隙锁
行锁是在数据表行记录上添加的锁,并不能锁住间隙,如果有INSERT操作,一样可以执行成功,此时就出现了幻读问题,为了解决幻读的问题,引入了间隙锁Gap Lock。间隙锁,就是在行与行之间的间隙处也增加了锁,它锁住的是一个范围区间,范围左右都是开区间。

来看一个例子,现有一张user表,分别有id(主键索引)、name、age三个字段,有以下1条数据:

idnameage
1a15

假设没有间隙锁,加锁时只针对记录加行锁,来看一个例子:

  1. 事务A在T1时刻查询age为15的数据,这里使用for update表示当前读,并且对这条记录加锁,此时可以查到一条记录;

  2. T2时刻,事务B又新增了一条age为15的数据,并进行了提交;

  3. T3时刻事务A中再查询时,使用了当前读,会发现可以查到两条记录,多出了一条age为15(事务B提交的那条)的数据,与T1时刻的数据不一致,此时就产生了幻读;

为什么使用for update进行当前读?

因为MySQL默认隔离级别是可重复读,如果不使用for update进行当前读,事务开启时创建一致性视图,使用的是快照读,所以读不到本事务开启后其他事务所做的操作,不会出现幻读。
而for update每次都要读取最新的数据,所以会出现幻读问题。

为什么会出现幻读?

for update已经加了写锁,按理说age为15的数据应该都会被锁住才对,为什么还可以新增一条age为15的数据?
因为行锁只能锁住某行数据,由于age字段上没有加索引,会锁住所有行,但是并没有锁住行之间的间隙,此时新增的那条数据还不存在,可以利用间隙这个漏洞新增一条age为15的数据。

为了解决幻读问题,引入了间隙锁,假设当前表中有三条数据,age分别为15、20、25:

idnameage
1a15
2b20
3c25

此时会存在四个间隙:
(-∞,15)、(15,20)、(20,25)、(25,+∞)

如果此时在age上执行查询(for update当前读,会加写锁):

select * from test where age = 15 for update;

因为age列没有添加索引,mysql会锁住所有行以及行之间的间隙,同一个时刻另外一个事务再执行INSERT语句:

insert into user(id, name, age) values(2, b, 15);

由于所有的区间都加了锁,此时会被阻塞,这样就防止了幻读。

需要注意间隙锁在在可重复读隔离级别下才会生效。

临键锁next-key lock

行数锁住的是某行记录,间隙锁锁的是行之间的间隙(左右都是开区间),将行数和间隙锁结合起来就是临键锁next-key lock,每个next-key lock都是左开右闭区间,以上面为例,next-key lock的所有区间为:

(-∞,15]、(15,20]、(20,25]、(25,+supremum]

InnoDB会为每个索引增加一个不存在的最大值supremum。

在可重复读隔离级别下,MySQL的加锁基本单位是临键锁,不过有两个优化:

  1. 索引上进行等值查询,如果是唯一索引,临键锁将会退化为行锁;
  2. 索引上进行等值查询,向右遍历时且最后一个值不满足等值条件时,临键锁退化为间隙锁;

第一个优化比较好理解,因为是唯一索引,为了提高性能,可以退化为行锁,只需要对那条数据加锁即可。

来看第二个优化,还是以上面的user表为例,有id(主键索引)、name(未添加索引)、age(添加了索引,注意与上面的例子区别,这里加了索引)三个字段,此时有以下数据:

idnameage
0aaa0
1a15
2b20

此时next-key lock区间为:
(0,15]、(15,20]、(20,+supremum]

  1. T1时刻使用lock in share mode从user表中查询age是15的id;
    由于默认加锁单位是临键锁,此时会给(0,15]这个区间加临键锁,由于age列是普通索引,需要继续向右遍历区间,查到age为20停止,访问到的对象都要加锁,
    所以本应该对(15,20]这个区间也加锁,但是根据优化2,这个区间的最后一个值20不满足age=15这个等值条件,所以退化为间隙锁(15,20)。

  2. T2时刻,向user表插入age为17的数据,由于对(0,15]和(15,20)这两个区间加锁,所以session B会进行阻塞;

MySQL在可重复读隔离级别下是否能解决幻读问题?

答案是不能,MVCC机制可以实现读提交/可重复读两个级别,在可重复读隔离级别下,如果使用快照读,确实可以避免出现幻读的问题

  1. 事务A在T1时刻查询age为15的数据,注意这里是普通的select语句是快照读,此时可以查到一条记录;

  2. T2时刻,事务B又新增了一条age为15的数据,并进行了提交;

  3. T3时刻事务A中再查询时,同样使用快照读,由于是从一致性视图中读取,并不会读到事务B提交的数据;

在可重复读隔离级别下,如果使用当前读,由于临键锁和间隙锁的存在,也可以避免幻读,上面的临键锁和间隙锁的例子都可以说明

不过这并不等价于在可重复读隔离级别下就可以避免幻读的问题,来看一个例子:

  1. 事务A在T1时刻查询age为15的数据,注意这里是普通的select语句是快照读,此时可以查到一条记录;

  2. T2时刻,事务B又新增了一条age为15的数据,并进行了提交;

  3. T3时刻事务A中再查询时,使用for update进行当前读,此时依旧可以查询到事务B提交的数据,所以就出现了幻读;

所以如果有快照读又有当前读的情况下,并不能解决幻读问题。

死锁

在使用锁的过程中,如果不同线程之间出现循环依赖资源,都在互相等待对方释放锁,就有可能造成死锁。

同样以上面的user表为例:

  1. T1时刻事务A更新id为1的数据,会对id为1的行加锁;
  2. T2时刻事务B更新id为2的数据,会对id为2的行加锁;
  3. T3时刻事务A同样去更新id为2的数据,此时已被事务B加锁,只能等待;
  4. T4时刻事务B需要更新id为1的数据,而这行数据已经被事务A加锁,事务B只能等待;
  5. 事务A和事务B互相等待对方占有的锁,形成循环,造成死锁;

对于死锁的解决方案有以下两种:
(1)通过innodb_lock_wait_timeout指定超时时间,默认值是50s,如果在某个时间内还没有获取到锁就超时放弃。
(2)将innodb_deadlock_detect设置为on开启死锁检测,每个事务到来被锁阻塞的时候,都会检测是否有可能导致死锁,当然开启死锁检测是有性能消耗的,高并发情况下需要消耗大量的CPU资源。

参考
极客时间 — 林晓斌(丁奇):MySQL实战

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

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

相关文章

python入门:分支结构

嗨喽~大家好呀,这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 1.内容导图 2.流程图介绍 绘制要求:不能出现程序语言的语法 3.百分制转五级计分制 分支结构:语句块,冒号缩进表示归属 单分支…

CRM系统的销售管理功能,你了解多少?

很多时候,CRM系统直接被当做是销售管理系统,其实无可厚非,因为CRM系统本身是围绕销售逻辑来实现的,不仅可以有效的优化商机,对商机的把握也是十分精确。下面我们就来详细说说,CRM系统都有哪些销售管理功能&…

java语言开发B/S架构医院云HIS系统源码【springboot】

医院云HIS全称为基于云计算的医疗卫生信息系统( Cloud- Based Healthcare Information System),是运用云计算、大数据、物联网等新兴信息技术,按照现代医疗卫生管理要求,在一定区域范围内以数字化形式提供医疗卫生行业数据收集、存储、传递、…

快来看呦!制作3D翻页产品宣传册原来这么受欢迎!

制作3D翻页产品宣传册不但使产品在表达效果上看上去更为绚丽多彩,并且具备比较强的立体视觉效果,增加大家浏览观看的吸引力,而且还便于传播,短时间里增加品牌的影响力。 那么,我们应该如何制作3D翻页产品宣传册&#x…

如何显示标注的纯黑mask图

文章目录 前言一、二分类mask显示二、多分类mask显示 前言 通常情况下,使用标注软件标注的标签图看起来都是纯黑的,因为mask图为单通道的灰度图,而灰度图一般要像素值大于128后,才会逐渐显白,255为白色。而标注的时候…

springboot运维篇--springboot项目打包

1.springboot项目jar方式部署 直接使用idea 直接使用命令 跳过测试用例快速打包 mvn clean -D skipTests package将这个应用打成jar包,直接使用java-jar的命令执行 需要注意的是,需要 jar 命令行启动需要依赖 Maven 插件的支持,必须确认在…

23种设计模式 - 模板方法模式

1. 认识模板方法模式 1.1 模式定义 定义一个操作算法中的框架,而将这些步骤延迟加载到子类中。 它的本质就是固定算法框架。 1.2 解决何种问题 让父类控制子类方法的调用顺序 模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 1.3…

如何实现公网远程访问本地OpenGauss数据库【内网穿透】

文章目录 前言1. Linux 安装 openGauss2. Linux 安装cpolar3. 创建openGauss主节点端口号公网地址4. 远程连接openGauss5. 固定连接TCP公网地址6. 固定地址连接测试 前言 openGauss是一款开源关系型数据库管理系统,采用木兰宽松许可证v2发行。openGauss内核深度融合…

芸鹰蓬飞:抖音投流以后还有自然流量吗?

随着抖音平台的普及,企业和个人纷纷加入到这个短视频的热潮中。然而,一旦投入抖音投流,是否还能依赖自然流量?这是许多用户和品牌关心的问题。本文将深入剖析这一话题,探讨抖音投流与自然流量之间的关系。 一、抖音投…

SpringBoot项目中ModelMapper配置以及使用

这里总结一下ModelMapper的使用方式,供大家参考 前言 项目中对象与对象赋值转换使用的频率非常的高,比如数据库表实体对象(Entity)与业务类对象(Model)之间的赋值传递,或者模型对象(Model)与视图对象(View…

报错:Parsing error: No Babel config file detected...的解决方案

报错:Parsing error: No Babel config file detected for E:\前端学习资料\9.vue基础\class\day03\02-源代码\01-component\vue.config.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the confi…

EasyPOI实现excel文件导出

EasyPOI真的是一款非常好用的文件导出工具&#xff0c;相较于传统的一行一列的数据导出&#xff0c;这种以实体类绑定生成的方式真的非常方便&#xff0c;也希望大家能够了解、掌握其使用方法&#xff0c;下面就用一个实例来简单介绍一下EasyPOI的使用。 1.导入依赖 <!-- e…

Autosar模块介绍:Memory_3(MemIf-内存接口抽象)

上一篇 | 返回主目录 | 下一篇 Autosar模块介绍&#xff1a;Memory_3(MemIf-内存接口抽象 1 基本术语解释2 MemIf组成结构图 1 基本术语解释 编号缩写原文解释1(Logical) Block——可单独寻址的连续内存区域&#xff08;即&#xff0c;用于读、写、擦除、比较等操作&#xff…

深度学习 python opencv 实现人脸年龄性别识别 计算机竞赛

文章目录 0 前言1 项目课题介绍2 关键技术2.1 卷积神经网络2.2 卷积层2.3 池化层2.4 激活函数&#xff1a;2.5 全连接层 3 使用tensorflow中keras模块实现卷积神经网络4 Keras介绍4.1 Keras深度学习模型4.2 Keras中重要的预定义对象4.3 Keras的网络层构造 5 数据集处理训练5.1 …

如何用python生成动态随机验证码图片

相信大部分小伙伴在登录注册页面都看到过这样的验证码图片&#xff1a; 今天就带大家用实现一波这种验证码图片的生成&#xff0c;这在Django开发中可以拿来即用~ 1. 首先导入必要的库&#xff1a; import random from PIL import Image, ImageDraw, ImageFont, ImageFilter…

Vatee万腾科技决策力的引领创新:Vatee数字化视野的崭新天地

在数字时代的激烈竞争中&#xff0c;Vatee万腾以其科技决策力的引领&#xff0c;开创了数字化视野的崭新天地。这并不仅仅是一场技术的飞跃&#xff0c;更是一次对未来的深刻洞察和引领创新的勇敢实践。 Vatee万腾的科技决策力不仅仅停留在数据分析和算法的运用&#xff0c;更是…

reticulate | R-python调用 | 安装及配置 | conda文件配置

reticulate | R-python安装及配置 | conda文件配置 1. 基础知识2. 安装reticulate from CRAN3. 包含了用于Python和R之间协同操作的全套工具&#xff0c;在R和Rstudio中均可使用4. 配置python环境4.1 4种环境配置方式4.2 miniconda 环境install_miniconda()报错一install_minic…

设计模式之原型模式(Prototype)

原型模式 如果已经有一个对象了&#xff0c;你想创建一个对象&#xff0c;而且对象里面的属性和已经存在的对象的属性差不多&#xff0c;就可以使用clone方法 克隆一个出来 实现原型模式需要实现标记型接口Cloneable -->标记型接口 : 里面没有需要实现的方法(空接口) 一般…

双十一钜惠!三门不可多得的HarmonyOS学习教程

今年双十一&#xff0c;各大商城优惠不断。这里介绍三门不可多得的HarmonyOS学习教程&#xff0c;都有非常大的折扣优惠。 《鸿蒙HarmonyOS手机应用开发实战》 《鸿蒙HarmonyOS手机应用开发实战》是由清华大学出版社出版的。 目前当当是“7.56折”&#xff1a;http://produc…

照片放大软件 Topaz Gigapixel AI mac中文版简介

Topaz Gigapixel AI mac是一款使用人工智能功能扩展图像的桌面应用程序&#xff0c;同时添加自然细节以获得惊人的效果。使用深度学习技术&#xff0c;A.I.Gigapixel™可以放大图像并填写其他调整大小的产品遗漏的细节&#xff0c;使用A.I.Gigapixel™&#xff0c;您可以裁剪照…