MySQL Innodb 中的排它锁、共享锁、意向锁、记录锁、间隙锁、临键锁、死锁讲解

一、MySQL 锁机制

MySQL作为流行的关系型数据库管理系统之一,在处理并发访问时,锁起着至关重要的作用。锁的使用可以确保数据的完整性,同时也是实现并发操作的必备工具。在MySQL Innodb 引擎中锁可以理解为两个方向的东西,一个是基本锁的类型一个是锁粒度的策略

对于锁的类型主要为我们常见的排他锁共享锁,排他锁又称独占锁,允许事务修改数据并阻止其他事务同时获取相同资源任何类型的锁。用于保护数据的完整性和一致性,确保在修改数据时不会发生冲突。共享锁允许多个事务同时读取同一行的数据,但不允许任何事务修改数据。

锁粒度的策略则是建立在排他锁和共享锁之上的方案。主要控制锁的粒度大小,实际锁的方式还是共享锁或排他锁。例如记录锁、间隙锁、临键锁等,不同的策略锁的粒度范围不同,如记录锁会锁住一行数据,间隙锁会锁住一个范围的数据等,本文将深入探讨MySQL Innodb 引擎中的锁机制。

二、基本锁的类型

2.1 排它锁

排他锁又称为写锁,简称X锁,是一种悲观锁,具有悲观锁的特征,如一个事务获取了一个数据行的X锁,其他事务尝试获取锁时就会等待另一个事务的释放。其中在 InnoDB 引擎下做写操作时 (UPDATE、DELETE、INSERT)都会自动给涉及到的数据加上 X 锁,因此当多线程情况下对同一条数据进行更新,在MySQL中不会出现线程安全问题。

其中 SELECT 语句默认不会加锁,如果查询的数据已经存在 X 锁,则会返回其最近提交的数据,如果希望每次获取的数据都是更新后最新的数据,当存在有更新时,则等待更新完成后获取新的值,这种情况下就需要对 SELECT 语句也要存在 X 锁,其中 SELECT 语句加 X 锁的话需要使用 FOR UPDATE 语句。

比如:当前有一张表结构如下:

CREATE TABLE `lock` (`id` int NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

写入一条测试数据:

INSERT INTO `testdb`.`lock`(`id`, `name`) VALUES (1, 'lock1');

下面,我使用 Navicat 开启了两个对话框,我在第一个对话框中,使用手动提交事务的方式执行更新语句,并且既不提交也不回滚事务:

BEGIN;
UPDATE `lock` SET `name` = 'lock2' WHERE id = 1; 

在这里插入图片描述
下面在另一个对话框中,查询 id = 1 的数据:

SELECT * FROM `lock` where id = 1

在这里插入图片描述
可以看到,并没有拿到最新的内容,因为此时 X 锁还没有释放,那此时对查询语句进行调整下,加上 FOR UPDATE 语句:

SELECT * FROM `lock` where id = 1 FOR UPDATE

在这里插入图片描述

此时会发现,查询语句一直在等待,因为这个查询语句在等待 X 锁的释放,下面对第一个对话框中,执行提交事务:

COMMIT;

在这里插入图片描述
在回到第二个对话框中查看:
在这里插入图片描述
已经拿到最新的值。这里需要注意下,你的是不是出现了超时报错,这是因为 Innodb 引擎对等待锁有个等待超时时间,默认情况下是 50s ,可以通过下面指令查看:

SHOW VARIABLES LIKE "Innodb_lock_wait_timeout"

在这里插入图片描述

如果感觉太小,可以通过下面指令调整:

SET innodb_lock_wait_timeout = 100

上面的操作已经感觉出来 X 锁的效果,那当两个 SELECT 语句都加上 FOR UPDATE 呢,比如在第一个回话框中,使用手动事务执行 SELECT 语句,同样不提交事务:

BEGIN;
SELECT * FROM `lock` where id = 1 FOR UPDATE;

在这里插入图片描述

在第二个对话框同样执行相同的代码,可以发现被阻塞掉了。

在这里插入图片描述

当第一个提交事务后,第二个紧接着也查出了信息,这也正符合排他锁的特征。

2.2 共享锁

共享锁可以理解为读锁,简称S锁,可以对多个事务SELECT情况下读取同一数据时不会阻塞,但是如果存在写操作时 (UPDATE、DELETE、INSERT),SELECT语句也会被阻塞,在MySQL中使用 S 锁需要使用 LOCK IN SHARE MODE

例如还是开启两个对话框,在第两个对话框中,都查询 id = 1 的数据,并加上 S 锁,最后同样不提交事务:

BEGIN;
SELECT * FROM `lock` where id = 1 LOCK IN SHARE MODE;

在这里插入图片描述
可以发现两个都拿到了数据,对两个都提交事务后,假如第一个对话框中是更新操作,最后同样不提交事务:

BEGIN;
UPDATE `lock` SET `name` = 'lock3' WHERE id = 1 ;

在这里插入图片描述
在第二个对话框中还是加上 S 锁的查询操作:

BEGIN;
SELECT * FROM `lock` where id = 1 LOCK IN SHARE MODE;

在这里插入图片描述

可以看到查询被阻塞了,当第一个对话框中提交了事务,这里才会返回结果:

在这里插入图片描述

2.3 意向锁

MySQL中的意向锁是一种特殊类型的锁,用于在表级别上表示事务可能要对表中的某些行进行修改。这种锁可以协调多个事务并发访问同一表,以确保数据的完整性和一致性。

意向锁分为两种类型:意向共享锁(IS)和意向排他锁(IX)。它们是在行级锁之上的一种辅助锁,用于表级锁的管理,意向锁是一种不与行级锁冲突的表级锁。

  • 意向共享锁(IS):表示事务打算在表中的某些行上设置共享锁(即读锁),但不是在整个表上设置排他锁(即写锁)。其他事务可以同时获取意向共享锁和共享锁,但不能获取排他锁。

  • 意向排他锁(IX):表示事务打算在表中的某些行上设置排他锁(即写锁),但不是在整个表上设置共享锁。其他事务可以同时获取意向排他锁和共享锁,但不能获取排他锁。

意向锁的引入主要是为了协调事务对表级锁的请求。当一个事务要在某一行上设置锁时,它会首先尝试获取意向锁。如果其他事务已经在表上设置了排他锁,则意向锁会阻止其他事务再获取共享锁,从而避免了读取到不一致的数据。同样,如果其他事务已经在表上设置了共享锁,则意向锁会阻止其他事务再获取排他锁,从而避免了并发写操作导致的数据破坏。

意向锁之间是互相兼容的

IXIS
IX兼容兼容
IS兼容兼容

意向锁和排他锁、共享锁之间只有共享级别的锁兼容:

XS
IX冲突冲突
IS冲突兼容

例如:在事务1中写入一条数据:

BEGIN;
INSERT INTO `lock`(id,name) VALUES(11,"lock1");

然后查询锁的情况:

SELECT * FROM	`performance_schema`.data_locks  WHERE OBJECT_NAME = 'lock'

在这里插入图片描述

可以看到加了表级意向排他锁。

然后在事务2中准备锁表:

LOCK TABLE `lock` WRITE;

在这里插入图片描述

可以看出被阻塞了。

三、锁粒度的策略

3.1 记录锁

记录锁(Row Lock)也叫行锁,将锁的粒度控制在最小的一行数据上,是一种用于控制并发访问的锁定机制,它允许多个事务同时操作同一张表中的不同行,而不会相互干扰,从而提高并发能力。记录锁包含了共享锁和排它锁,锁定的资源是索引记录,如果表中的字段没有索引,InnoDB 会创建一个隐藏的聚集索引并使用该索引进行记录锁定。

例如:在事务1中,查询所有数据:

BEGIN;
SELECT * FROM `lock` FOR UPDATE;

然后查看锁的情况:

SELECT * FROM	`performance_schema`.data_locks  WHERE OBJECT_NAME = 'lock'

在这里插入图片描述

LOCK_DATA 可以看出锁的资源是主键ID

3.2 间隙锁

间隙锁(Gap Lock)是 MySQL 中一种特殊类型的锁,用于在事务中防止其他事务插入新记录或修改范围内的数据,保证数据的一致性和防止幻读。间隙锁通常与范围条件查询结合使用,确保数据的完整性和一致性。

例如:当事务 1 查询 id > 0 and id < 5 的数据时,如果此时事务 2 对该范围的数据写入了一条数据,而这个数据如果在事务1中继续操作,就有可能出现幻读。

所以为了避免这个问题,InnoDB 引擎中引入了间隙锁机制,即在索引中的两个值之间锁定一个间隙,阻止其他任何事务在该间隙上进行插入或修改操作,从而保证数据的一致性和防止幻读。

注意:间隙锁只在 Innodb 引擎可重复读隔离级别中存在。

例如,查询一个范围:

BEGIN;
SELECT * FROM `lock` where id > 1 and id < 5 FOR UPDATE

然后查看锁情况:

SELECT * FROM	`performance_schema`.data_locks  WHERE OBJECT_NAME = 'lock'

在这里插入图片描述

可以看到锁住了2-5这个范围的数据,细心的可以发现 5 其实没有在查询条件中,但是也被锁住了,这就和间隙锁的范围有关了,间隙锁会锁住索引之间的数据或者第一个索引前面或者最后一个索引后面。这里 5 就是最后一个索引后面的数据。

注意,如果根据 id > 13,此时id最大值是11的情况下,则会锁住 11 - 正无穷 的范围:

BEGIN;
SELECT * FROM `lock` where id > 13 FOR UPDATE

查看锁情况:

SELECT * FROM	`performance_schema`.data_locks  WHERE OBJECT_NAME = 'lock'

在这里插入图片描述

看到LOCK_DATAsupremum pseudo-record,它是 InnoDB 中定义的一种特殊记录,我们可以理解为 +∞

如果此时写入一个id=15的数据,就会被阻塞:

BEGIN;
insert into `lock`(id,name) values(15,'lock');

在这里插入图片描述

3.3 临键锁

临键锁是索引记录上的记录锁(Record Locks)和索引记录之前的间隙上的间隙锁(Gap Locks)的组合。也就是临键锁不仅会用记录锁锁住相关的行数据,也会用间隙锁锁住一个范围的数据。同样临键锁的目标也是保证数据的一致性和防止幻读。临键锁针对间隙范围时遵循左开右闭的原则。

具体触发策略为:

  • 当筛选字段是唯一索引时,进行等值查询时,针对目标数据增加记录锁。没有匹配到任何记录的时候,增加间隙锁。

  • 当筛选字段是普通索引时,进行等值查询时,针对目标数据增加记录锁,然后向右遍历直到最后一个值不满足查询条件时,这个范围增加间隙锁。

例如:给 name 字段添加普通索引:

ALTER TABLE `lock` ADD INDEX index_name(name);

然后进行普通索引等值查询:

BEGIN;
SELECT * FROM `lock` where name = 'lock1' FOR UPDATE

查看锁情况:

SELECT * FROM	`performance_schema`.data_locks  WHERE OBJECT_NAME = 'lock'

在这里插入图片描述
可以看到锁住了 lock1 的普通索引和主键索引,同时也将 lock1之后的普通索引 lock10给锁上了,锁的类型为间隙锁 。

然后当使用唯一索引时,查询目标不存在情况下。

例如数据库中没有 id=10 的记录,但我们还要查询 id=10 的数据:

在这里插入图片描述

BEGIN;
SELECT * FROM `lock` where id = 10 FOR UPDATE

然后查看锁情况:

SELECT * FROM	`performance_schema`.data_locks  WHERE OBJECT_NAME = 'lock'

在这里插入图片描述

可以看到主键为 11 的数据被锁住了,锁的类型是间隙锁。

四、死锁

上面了解到了MySQL中的各种锁机制,既然存在锁肯定会有死锁的风险。例如事务1中更新了 id=1 的数据,事务 2 中更新了 id = 2 的数据,此时事务1 准备更新 id=2 的数据,而事务 2 准备更新 id=1 的数据,需要互相等待对方释放锁,此时就是死锁。

好在 MySQL 中默认开启了死锁检测,当发现某个事物的操作可能会造成死锁时,会主动回滚当前事务。

可以通过下面指令查看是否开启:

SHOW GLOBAL VARIABLES LIKE 'innodb_deadlock_detect';

在这里插入图片描述

例如:在事务1中查询 id=1 的数据,并添加排他锁:

BEGIN;
SELECT * FROM `lock` where id = 1 FOR UPDATE;

此时事务2,查询 id=2 的数据,并添加排他锁:

BEGIN;
SELECT * FROM `lock` where id = 2 FOR UPDATE;

然后事务1中,又查询 id=2 的数据,添加排他锁:

SELECT * FROM `lock` where id = 2 FOR UPDATE;

由于id=2的锁被事务2持有,此时会阻塞等待:

在这里插入图片描述

紧接着又在事务2中,查询 id=1 的数据,添加排他锁:

SELECT * FROM `lock` where id = 1 FOR UPDATE;

此时可以发现事物2异常回滚了,给出了提示是发现了死锁:

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MES生产管理系统:私有云、公有云与本地化部署的比较分析

随着信息技术的迅猛发展&#xff0c;云计算作为一种新兴的技术服务模式&#xff0c;已经深入渗透到企业的日常运营中。在众多部署方式中&#xff0c;私有云、公有云和本地化部署是三种最为常见的选择。它们各自具有独特的特点和适用场景&#xff0c;并在不同程度上影响着企业的…

lanqiao.602 迷宫

题目&#xff1a; 代码&#xff1a; #include<iostream> #include<cstring> #include<algorithm> #include<queue> using namespace std; char mp[31][51]; //稍微开大一点 char k[4]{D,L,R,U}; //按字典序记录路径 int dirx[]{1,0,0,-1},d…

【ARM 裸机】汇编 led 驱动之原理分析

1、我们为什么要学习汇编&#xff1f;&#xff1f;&#xff1f; 之前我们或许接触过 STM32 以及其他的 32 位的 MCU ,都是基于 C 语言环境进行编程的&#xff0c;都没怎么注意汇编&#xff0c;是因为 ST 公司早已将启动文件写好了&#xff0c;新建一个 STM32 工程的时候&#…

transformer上手(3) —— 开箱即用的 pipelines

1 开箱即用的 pipelines Transformers 库将目前的 NLP 任务归纳为几下几类&#xff1a; 文本分类&#xff1a;例如情感分析、句子对关系判断等&#xff1b;对文本中的词语进行分类&#xff1a;例如词性标注 (POS)、命名实体识别 (NER) 等&#xff1b;文本生成&#xff1a;例如…

centos编译安装nginx1.24

nginx编译1.24&#xff0c;先下载安装包 机器通外网的话配置nginx的yum源直接yum安装 vim /etc/yum.repos.d/nginx.repo [nginx-stable] namenginx stable repo baseurlhttp://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck1 enabled1 gpgkeyhttps://nginx.org…

Android 包命名规范

Android包目录的命名规范会直接影响到整个APP攻城后期的开发效率和拓展性。 常用两种命名方式&#xff1a;PBL&#xff08;package by layer ) 和PBF(pakcage by Feature) layer 英/ˈleɪə(r)/ 翻译&#xff1a;层 feature 英/ˈfiːtʃə(r)/ 翻译&#xff1a;特色 1 Pac…

【MySQL】MySQL在Centos 7环境安装

目录 准备工作 第一步&#xff1a;卸载不要的环境 第二步&#xff1a;下载官方的mysql 第三步 上传到Linux中 第四步 安装 正式安装 启动 ​编辑 登录 准备工作 第一步&#xff1a;卸载不要的环境 使用root进行安装 如果是普通用户&#xff0c;使用 su - 命令&#…

RUM 最佳实践-交互延迟的探索与发现

FID 在互联网高速发展的时代&#xff0c;用户体验已成为企业竞争的关键所在。网页性能作为用户体验的重要组成部分&#xff0c;直接影响着用户的满意度和工作效率。First Input Delay&#xff08;FID&#xff09;作为衡量网页性能的重要指标&#xff0c;越来越受到业界关注。今…

走进MySQL:从认识到入门(针对初学者)

一&#xff0c;引言 MySQL是一款久负盛名且广泛应用的关系型数据库管理系统&#xff0c;自1995年Michael Widenius和David Axmark在瑞典和芬兰发起研发以来&#xff0c;其发展历程可谓辉煌且深远。作为开源软件的代表&#xff0c;MySQL以其卓越的成本效益、高性能及高可靠性赢得…

前端实现自动获取农历日期:探索JavaScript的跨文化编程

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

【ELK】ELK企业级日志分析系统

搜集日志&#xff1b;日志处理器&#xff1b;索引平台&#xff1b;提供视图化界面&#xff1b;客户端登录 日志收集者&#xff1a;负责监控微服务的日志&#xff0c;并记录 日志存储者&#xff1a;接收日志&#xff0c;写入 日志harbor&#xff1a;负责去连接多个日志收集者&am…

YOLOv5 / YOLOv7 / YOLOv8 / YOLOv9 / RTDETR -gui界面-交互式图形化界面

往期热门博客项目回顾&#xff1a;点击前往 计算机视觉项目大集合 改进的yolo目标检测-测距测速 路径规划算法 图像去雨去雾目标检测测距项目 交通标志识别项目 yolo系列-重磅yolov9界面-最新的yolo 姿态识别-3d姿态识别 深度学习小白学习路线 AI健身教练-引体向上…