MySQL的共享锁和排他锁

news2025/1/16 3:45:19

锁定读 Locking Reads

有过编程语言并发学习经验的同学,应该都了解过读写锁的概念。读写锁主要是为了解决多读少写条件下,程序的并发性能问题。它的特点即是:如果一个线程持有了读锁,那么其他线程也是可以继续读取它锁定的数据,但是不能进行修改,即加写锁;如果一个线程持有了写锁,那么它会阻止任何其他线程的读取和修改。在数据库领域,也有读写锁的概念,下面让我们来了解一下。

在一个事务中,查询数据并且插入或者更新相关的数据,使用常规的 SELECT 语句并不能提供足够的保护。其他事务可以更新或者删除你刚好查询的数据行。因此,InnoDB 支持了两种不同类型的锁定读来提供额外的安全性。Locking Reads 这里我把它称为锁定读,它的作用就是在查询数据时加上读锁或者写锁。

  • 共享锁 Shared Lock,又称为读锁,简称 S 锁
    允许一个事务读取数据,但不允许其他事务修改数据。共享锁通常用于查询操作,以确保查询期间不会有事务修改数据。
  • 排他锁 Exclusive Lock,又称为写锁,简称 X 锁
    允许一个事务读取和修改数据,但不允许其他事务读取或者修改数据。排他锁通常用于更新或者删除操作,以确保在事务完成之前不会有其他事务修改数据。

注: 我觉得 Exclusive Lock 翻译成独占锁也挺好的。

共享锁和排他锁针对的是数据行级别的锁,而不是针对的整个表的锁。不过,在某些情况下,它也会从行锁退化成表锁(这是很严重的问题,应该极力避免它的发生)。

因此在一个事务中查询数据时,需要根据需要来使用共享锁或者排他锁:

  • 查询加共享锁:SELECT ... FOR SHARE
  • 查询加排他锁:SELECT ... FOR UPDATE

注意:锁定读必须在事务语句中才可以生效,或者关系事务的自动提交。

SELECT ... FOR SHARE

在一个事务中,给查询的数据行设置一个共享锁。在事务提交之前,其他的事务(或者会话)可以读取这些数据行,但是不能修改它们。如果这些数据行中的任何数据被其他事务修改了并且没有提交,你的加锁查询必须等待,直到其他事务结束,并且会获取到最新的值。

SELECT ... FOR SHARESELECT ... LOCK IN SHARE MODE 的替代,但是为了向后兼容后者依然可以使用,所以这两句是等价的。因为我这里使用的 MySQL 是 8.0 的版本,所以参考的文档也是 8.0 的。

SELECT ... FOR UPDATE

在一个事务中,给查询的数据行设置一个排他锁,对于搜索遇到的索引记录,它会锁定数据行和相关的索引项,这与使用 UPDATE 语句的效果是相同的。其他事务会被阻止更新(UPDATE)这些数据行、执行查询加共享锁(SELECT ... FOR SHARE)或者读取在某些隔离级别的数据。一致性读取(Consistent Read)会忽略在读取视图中存在的记录上设置在数据行上的任何锁定。(旧版本的记录行无法被锁定;它们是通过 undo logs 重建在记录的内存副本上的)。

注:后面这个一致性读取涉及另一个 MySQL 的重要特性:MVCC,多版本并发控制

所有通过 FOR SHAREFOR UPDATE 查询设置的锁,在事务被提交或者回滚时都会被释放。

注意:外部查询语句的锁定读不会锁定子查询语句,除非在子查询语句中也使用锁定读。下面是两个官方文档的例子:

这条语句不会锁定表 t2 中的行:

SELECT * FROM t1 WHERE c1 = (SELECT c1 FROM t2) FOR UPDATE;

如果要锁定表 t2 中的行,在子查询中使用锁定读语句:

SELECT * FROM t1 WHERE c1 = (SELECT c1 FROM t2 FOR UPDATE) FOR UPDATE;

实践

这里来分别实践一下加读锁和加写锁的场景,以及这个过程。

测试表结构:现在是有一个用户账户表(t_user_account),现在再添加一个用户信息表(t_user_info),它的 account_id 字段是用户账户表的主键。

CREATE table t_user_account(
	id INT NOT NULL AUTO_INCREMENT,
    name VARCHAR(20) NOT NULL,
    balance INT NOT NULL,
    PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


CREATE table t_user_info(
	id INT NOT NULL AUTO_INCREMENT,
    account_id INT NOT NULL,
    PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

查询加读锁

那么现在我需要给用户信息表添加一条记录,这样会有问题吗?这里显然是需要事务的,因为这是多条 SQL 语句了,但是这样就高枕无忧了吗?

在这里插入图片描述

在这里插入图片描述

如果在第一个事务执行查询之后,第二个事务把它查询的数据给删除了呢?这样就会失去参照完整性(referential integrity)了。所以,为了确保在第一个事务执行完成之前,它所依赖的数据不被修改,我们需要加读锁(当然可以加写锁了,但是加了写锁其他事务就无法读取了,影响系统的性能)。如下图,加了写锁之后,第二个事务尝试去删除对应的记录就会被卡住(默认的超时时间是 50s)。

在这里插入图片描述

查询加写锁

这里来模拟一个并发存取钱的场景:

事务 A:查询 id = 1 的用户的余额,然后模拟存 200。
事务 B:查询 id = 1 的用户的余额,然后模拟取 400。

如果这里不加写锁,那么我们来看一下可能出现的错误:

在这里插入图片描述

由于可重复读的原因,第二个事务读取的是它那个时间点数据的快照(MVCC),这样就会导致最终的金额是错误的,两个事务对同一个金额进行修改。也就是说,在第一个事务读取到金额之后,应该阻止其他的事务进行读取,在它执行结束后,才能允许其他事务操作。所以这里需要加写锁,下面是在事务开始之后加写锁之后的执行效果,最终的结果是正确的。这样 id = 1 的这行记录就会被锁住,其他事务是无法对其进行查询(加锁 SELECT)和修改(UPDATE、DELETE)。

在这里插入图片描述

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

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

相关文章

PCIE WIFI与金手指转接设计

PCIE转接口设计: 金手指转接设计 类似的芯片的框图:

Python 利用Matplotlib制作初中时圆规画的图

背景 大家在初中时,开始学习圆相关的知识,涉及圆的半径、周长、面积 等等,那会每位同学基本都会买一套圆规、三角板,来辅助学习和做作业使用,这些学习工具在闲暇时光也被用来玩耍,偶然间就拿着圆规在纸上画…

【零散技术】10分钟学会 Odoo Widget many2many_tags的使用与拓展

序言:时间是我们最宝贵的财富,珍惜手上的每个时分 1.基本使用 widget “many2many_tags”是我们常用的视图组件,使用后会badge形式展示数据,未使用widget则只显示 (x记录) 2.自定义显示内容 使用 many2many_tags后默认显示模型中的…

Oracle报错 PLS-00103: 出现符号 ““在需要下列之一时

在IDEA中执行以下SQL时,报了这个异常 检查了语法,你会发现语法没有任何问题,标点也没有任何问题。 罪魁祸首在这: 换行符为CRLF,我们需要改成LF 即可执行成功

护眼灯买什么样的好?分享五款护眼灯

护眼台灯的光照一般比较均匀,相比普通台灯,一般具有防蓝光、防频闪等功能,能够提供一个健康舒适的学习、生活灯光环境,建议选购内置智能感光模式的护眼台灯,以确保灯光亮度一直处于均衡状态,让眼睛更轻松。…

ARDUINO STM32 SSD1306

STM32F103XX系列SPI接口位置 在ARUDINO 下,(不需要设置引脚功能,不需要开启时钟设置,ARDUINO已经帮我们处理了) stm32f103c6t6 flash不足,不足以运行U8G2,产生错误 改用U8X8,后将字体改为u8x8_…

5V升压充电8.4V管理IC

在我们小家电设计当中USB口的5V输入升压到8.4V输出,使用一颗SOP8的升压充电芯片,直接升压到8.4V.电流在1A左右。2,USB输入,5V升压8.4V,充电1A,内含专门的双节锂电池充电管理逻辑和LED指示灯,我们…

大学生该怎么认清当下的就业环境呢?

大学生毕业后进入职场,面临的就业环境也在不断发生变化。为了更好地适应这个变化莫测的环境,大学生需要认清当下的就业环境,并做出相应的应对策略。 了解行业趋势,抓住就业机会 如今,各行各业的竞争日益激烈&#xff…

使用nps实现内网穿透

1、介绍 ​ 当我们想把内网的一些资源暴露在公网上时,可以使用内网穿透功能。比如公司的内网服务器,部署了平时需要开发的项目,但是回到家中无法访问,就可以使用内网穿透,将公司内网的接口映射到一台公网的服务器上&a…

现浇钢筋混泥土楼板施工岗前安全VR实训更安全高效

建筑行业天天与钢筋混凝土砼在,安全施工便成了企业发展的头等大事。 当今社会,人人都奉行生命无价,安全至上。可工地安全事故频繁发生,吞噬掉多少宝贵生命。破坏了多小个家庭?痛定死痛,为了提高施工人员的安全意识。 …

多目标优化

https://zhuanlan.zhihu.com/p/158705342 概念 单目标优化只有一个优化目标,所以可以比较其好坏。 但是多目标优化,在需要优化多个目标时,容易存在目标之间的冲突,一个目标的优化是以其他目标劣化为代价的,所以我们要…

CUDA小白 - NPP(2) -图像处理-算数和逻辑操作(2)

cuda小白 原始API链接 NPP GPU架构近些年也有不少的变化,具体的可以参考别的博主的介绍,都比较详细。还有一些cuda中的专有名词的含义,可以参考《详解CUDA的Context、Stream、Warp、SM、SP、Kernel、Block、Grid》 常见的NppStatus&#xf…

Vue项目直接报错

最近自己在做一个vue2项目,vue并不熟悉,所以求解!!! 通过命令:vue create app 创建项目,但打开后,浏览器直接报错,意思为:不能在模块外使用import语句(at ho…

c++ opencv将彩色图像按连通域区分

要将彩色图像按连通域区分&#xff0c;您可以使用 OpenCV 中的 cv::connectedComponents 函数。 下面是一个简单的示例代码&#xff0c;说明如何使用 cv::connectedComponents 函数来检测并标记图像中的连通域&#xff1a; #include <opencv2/opencv.hpp> #include <…

智能制造产业链数字化转型、数字化互联工厂建设方案PPT

本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用&#xff0c;如有侵权请联系删除&#xff0c;更多浏览公众号&#xff1a;智慧方案文库 篇幅有限&#xff0c;无法完全展示&#xff0c;喜欢资料可转发评论&#xff0c;私信了解更多信息。

初次跑yolo5遇到的一些问题

1. ImportError: cannot import name COMMON_SAFE_ASCII_CHARACTERS‘ from charset-normalizerconstant‘ 这个报错可能是由于charset_normalizer模块的版本问题引起的。尝试更新charset_normalizer模块到最新版本&#xff0c;或者使用较旧的版本&#xff0c;看看是否可以解…

Java 中数据结构HashSet的用法

Java HashSet HashSet 基于 HashMap 来实现的&#xff0c;是一个不允许有重复元素的集合。 HashSet 允许有 null 值。 HashSet 是无序的&#xff0c;即不会记录插入的顺序。 HashSet 不是线程安全的&#xff0c; 如果多个线程尝试同时修改 HashSet&#xff0c;则最终结果是…

Android屏幕显示 android:screenOrientation configChanges

显示相关 屏幕朝向 https://developer.android.com/reference/android/content/res/Configuration.html#orientation 具体区别如下&#xff1a; activity.getResources().getConfiguration().orientation获取的是当前设备的实际屏幕方向值&#xff0c;可以动态地根据设备的旋…

恒运资本:市盈率怎么算?

市盈率&#xff08;P/E ratio&#xff09;是判别一家公司股票价格合理性的一个重要目标&#xff0c;也是投资者评估公司股票投资价值的重要参阅目标。市盈率越高&#xff0c;表明相对于公司的收益来说&#xff0c;该公司的股票定价越高。市盈率越低&#xff0c;则表明该股票被低…