Mysql加锁流程详解

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: Mysql加锁流程详解

前言


前面已经给大家分享了Mysql中有哪些锁、锁的分类以及相互间的兼容性。本节继续分享Mysql的加锁流程。


由于InnoDB引擎才支持行级锁,以下内容都是基于InnoDB引擎介绍。


一、锁的内存结构


对一条记录加锁本质上是内存中创建的一个锁结构跟这条记录相关联。

所以锁本质上就是内存中的一种数据结构。


那么我们在操作一个事务的时候,如果对应多条记录,是不是要针对多条记录生成多个内存的锁结构呢?比如我们执行select * from tb_user for update的时候,tb_user表中如果存在1万条数,那么难道要生成1万个内存的锁结构吗?那当然不会是这样的。其实,如果符合以下几个条件,那么这些记录的锁就可以放到一个内存中的锁结构里了,条件如下所示:


1.加锁操作时在同一个事务中

2.需要被加锁的记录在同一个页中

3.需要加锁的类型是一致的

4.锁的等待状态是一致的


那么这么多次的锁结构,它到底是怎么组成的呢?

主要是由6部分组成的。分别为:锁所在的事务信息、索引信息、表锁或行锁信息、type_mode、其他信息、与heap_no对应的比特位。如下图所示:

68.png


锁所在的事务信息

一个锁结构对应一个事务,那么这里就存储着锁对应的事务信息。它其实只是一个指针,可以通过它获取到内存中关于该事务的更多信息,比如:事务id是多少。


索引信息

对于行级锁来说,这里记录的就是加锁的记录属于哪个索引。


表锁/行锁信息

(1)、对于表锁,主要是来记录对哪张表进行的加锁操作以及其他的信息。

(2)、对于行锁,内容包括3部分:

Space ID:记录所在的表空间ID。

Page Number:记录所在的页号。

n_bits:一条记录对应一个bit


type_mode

它是由32个bit组成的,分别为:lock_mode、lock_type、lock_wait和rec_lock_type,如下图所示:

67.png


二、加锁流程


1、加锁的基本流程

66.png

【上图解释如下:】

(1)、一开始是没有锁结构与记录进行关联的,即:上图第一个图例所示。

(2)、当一个事务T1想对这条记录进行改动时,会看看内存中有没有与这条记录关联的锁结构,如果没有,就会在内存中生成一个锁结构与这条记录相关联,即:上图第二个图例所示。我们把该场景称之为获取锁成功或者加锁成功。

(3)、此时又来了另一个事务T2要访问这条记录,发现这条记录已经有一个锁结构与之关联了,那么T2也会生成一个锁结构与这条记录关联,不过锁结构中的is_waiting属性值为true,表示需要等待。即:上图第三个图例所示。我们把该场景称之为获取锁失败/加锁失败。

(4)、事务T1提交之后,就会把它生成的锁结构释放掉,然后检测一下还有没有与该记录关联的锁结构。结果发现了事务T2还在等待获取锁,所以把事务T2对应的锁结构的is_waiting属性设置为false,然后把该事务对应的线程唤醒,让T2继续执行。


2、根据主键加锁

65.png

对应sql语句,其中id字段是自增主键:


update user set age = 10 where id = 49;


说明:

1、基于主键(聚簇索引)进行等值查询时,如果对应的值存在,则只需添加标准记录锁Record Lock。如果对应的值不存在,则需要在查询id所在的索引间隙添加间隙锁Gap Lock。

2、基于主键(聚簇索引)进行范围查询时,采用采用Next Key Lock添加行锁。


3、根据二级索引加锁

64.png

对应sql语句,其中name字段上有普通索引:


update user set age = 10 where name = 'Tom';


说明:

1、基于辅助索引进行查询时,会先在辅助索引上加锁,然后在聚簇索引上加锁。

2、基于辅助索引进行查询时,聚簇索引上加锁算法采用Record Lock,即只锁记录不锁间隙。


4、根据非索引字段查询加锁

对应sql语句,其中age字段上没有索引:


update user set name = 'Tom' where age = 10;


说明:

1、查询不走索引时,会在聚簇索引上加锁,加锁算法采用Next Key Lock,并且会锁定全表范围。


注意是通过Next Key Lock锁定的全表范围,而不是通过表级锁直接锁表。


5、加锁规律

1.InnoDB中默认采用Next Key Lock加锁,Next Key Lock加锁范围前开后闭。

2.行锁都是加在索引上,如果通过聚集索引查询则在聚集索引上加锁,通过辅助索引查询则需要同时在辅助索引和聚集索引上加锁,不走索引则在聚集索引上加锁。

3.查找过程中访问到的索引才会加锁。注意是访问到的索引而不是满足查询条件的索引。

4.基于主键和唯一索引进行等值查询,Next Key Lock会退化为行锁Record Lock。

5.索引上的等值查询,没有满足条件的记录时,Next-key lock退化为间隙锁,加锁范围是查询值所在的间隙。

6.通过辅助索引查询并加锁时,需要进行回表查询然后在聚集索引上采用行锁Record Lock加锁。

7.范围查询采用Next Key Lock加锁。


三、影响锁的因素


数据库的隔离等级,SQL 语句和当前数据库数据会共同影响该条 SQL 执行时数据库生成的锁模式,锁类型和锁数量。

63.png


MySQL 的隔离等级对加锁有影响,所以在分析具体加锁场景时,首先要确定当前的隔离等级。


读未提交(Read Uncommitted 后续简称 RU):可以读到未提交的读,基本上不会使用该隔离等级,所以暂时忽略。

读已提交(Read Committed 后续简称 RC):存在幻读问题,对当前读获取的数据加记录锁。

可重复读(Repeatable Read 后续简称 RR):不存在幻读问题,对当前读获取的数据加记录锁,同时对涉及的范围加间隙锁,防止新的数据插入,导致幻读。

序列化(Serializable):从 MVCC 并发控制退化到基于锁的并发控制,不存在快照读,都是当前读,并发效率急剧下降,不建议使用。

这里说明一下,RC 总是读取记录的最新版本,而 RR 是读取该记录事务开始时的那个版本,虽然这两种读取的版本不同,但是都是快照数据,并不会被写操作阻塞,所以这种读操作称为 快照读(Snapshot Read)。


MySQL 还提供了另一种读取方式叫当前读(Current Read),它读的不再是数据的快照版本,而是数据的最新版本,并会对数据加锁,根据语句和加锁的不同,又分成三种情况:

SELECT ... LOCK IN SHARE MODE:加共享(S)锁
SELECT ... FOR UPDATE:加排他(X)锁
INSERT / UPDATE / DELETE:加排他(X)锁


当前读在 RR 和 RC 两种隔离级别下的实现也是不一样的:RC 只加记录锁,RR 除了加记录锁,还会加间隙锁,用于解决幻读问题。


在RR隔离级别下:

MVCC机制解决的是select查询的幻读问题,而通过当前读的方式是通过加间隙锁解决的幻读问题。


四、锁信息查看


1、查看锁的sql语句

-- 查看当前所有事务
select * from information_schema.innodb_trx;
-- 查看加锁信息(MySQL5.X)
select * from information_schema.innodb_locks;
-- 查看锁等待(MySQL5.X)
select * from information_schema.innodb_lock_waits;
--查看加锁信息(MySQL8.0)
SELECT * FROM performance_schema.data_locks;
--查看锁等待(MySQL8.0)
SELECT * FROM performance_schema.data_lock_waits;
-- 查看表锁
show open tables where In_use>0;
-- 查看最近一次死锁信息
show engine innodb status;

这里主要介绍通过查询performance_schema.data_locks表,查看事务中加锁的情况。


设置标签

DROP TABLE if EXISTS user;
CREATE TABLE `user` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `account` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '账号',
  `name` varchar(30) DEFAULT NULL COMMENT '姓名',
  `age` int DEFAULT NULL COMMENT '年龄',
  `email` varchar(50) DEFAULT NULL COMMENT '邮箱',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_account` (`account`) USING BTREE,
  KEY `ik_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `user` (`id`,`account`,`name`, `age`, `email`) VALUES (3, '000003', '老万', 12, '101@qq.com');
INSERT INTO `user` (`id`,`account`, `name`, `age`, `email`) VALUES (10, '000010', '老张', 15, '101@qq.com');
INSERT INTO `user` (`id`,`account`, `name`, `age`, `email`) VALUES (20, '000020', '老王', 15, '101@qq.com');
INSERT INTO `user` (`id`,`account`, `name`, `age`, `email`) VALUES (30, '000030', '老王', 30, '101@qq.com');


开启mysql命令行窗口,开启事务执行加锁:

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> UPDATE  `user` set age=18 WHERE id = 4;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0  Changed: 0  Warnings: 0

查看加锁情况:


SELECT * FROM performance_schema.data_locks;


执行结果:

62.png


2、data_locks表字段说明

字段介绍:


ENGINE 表使用的存储引擎,这里是InnoDB

ENGINE_TRANSACTION_ID 事务ID

OBJECT_SCHEMA 加锁的表空间,这里的表空间是test

OBJECT_NAME 加锁的表名,这里是user

INDEX_NAME加锁的索引名称,表级锁为null,行级锁为加锁的索引名称。这里PRIMARY表示是主键索引上添加锁。

LOCK_TYPE 锁类型:TABLE对应表级锁,RECORD对应行级锁。

LOCK_MODE 加锁模式,对应具体锁的类型,比如:IX 意向排他锁,X,GAP 排他间隙锁。

LOCK_STATUS 锁的状态,GRANTED 已获取,WAITING 等待中

LOCK_DATA 加锁的数据,这里的10表示,在主键索引值为10的记录上加锁。由于加的是间隙锁GAP,这里锁定的是3~10这个间隙。如果值为supremum pseudo-record,表示高于索引中的任何值,锁定正无穷的范围。


3、lock_mode说明


这里需要重点对 LOCK_MODE 加锁模式进行说明:

LOCK_MODE值

锁类型

IX

意向排他锁

IS

意向共享锁

AUTO_INC

自增主键锁

X

排他临键锁(Next Key) ,既锁记录,也锁间隙

S

共享临键锁(Next Key) ,既锁记录,也锁间隙

X,REC_NOT_GAP

排他标准记录锁(Record),只锁记录,不锁间隙

S,REC_NOT_GAP

共享标准记录锁(Record) ,只锁记录,不锁间隙

S,GAP

共享间隙锁(GAP),只锁间隙
X,GAP 排他间隙锁(GAP) ,只锁间隙
INSERT_INTENTION 插入意向锁



总结


本文主要对Mysql加锁流程进行了详细说明。

1、了解锁的内存结构,注意行锁是可以合并的,并不需要为每条记录都添加一个锁。

2、熟悉根据主键查询加锁,根据二级索引查询加锁,以及不走索引的查询的加锁规律。

3、通过data_locks表查看加锁信息。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
1月前
|
SQL 关系型数据库 MySQL
MySQL 锁
MySQL里常见的几种锁
52 3
|
16天前
|
存储 SQL NoSQL
|
26天前
|
存储 关系型数据库 MySQL
优化 MySQL 的锁机制以提高并发性能
【10月更文挑战第16天】优化 MySQL 锁机制需要综合考虑多个因素,根据具体的应用场景和需求进行针对性的调整。通过不断地优化和改进,可以提高数据库的并发性能,提升系统的整体效率。
47 1
|
1月前
|
关系型数据库 MySQL Java
MySQL数据锁:Record Lock,Gap Lock 和 Next-Key Lock
本文基于 MySQL 8.0.30 版本及 InnoDB 引擎,深入解析三种行锁机制:记录锁(Record Lock)、间隙锁(Gap Lock)和临键锁(Next-key Lock)。记录锁锁定索引记录,确保事务唯一修改;间隙锁锁定索引间的间隙,防止新记录插入;临键锁结合两者,锁定范围并记录自身,有效避免幻读现象。通过具体示例展示了不同锁的作用机制及其在并发控制中的应用。
106 2
|
1月前
|
存储 关系型数据库 MySQL
MySQL数据库锁:共享锁和独占锁
本文详细介绍了`InnoDB`存储引擎中的两种行级别锁:共享锁(S锁)与排他锁(X锁)。通过具体示例展示了这两种锁的工作机制及其在`InnoDB`与`MyISAM`引擎中的表现差异。文章还提供了锁的兼容性矩阵,帮助读者更好地理解锁之间的互斥关系。最后总结了两种锁的特点及适用场景。适合希望深入了解`MySQL`并发控制机制的读者阅读。
40 1
|
26天前
|
存储 关系型数据库 MySQL
MySQL锁,锁的到底是什么?
【10月更文挑战第16天】MySQL 锁锁定的是与数据和资源相关的对象,其目的是为了保证数据的一致性、避免冲突,并在并发环境下合理协调事务或操作的执行。理解锁的对象和意义对于优化数据库性能、处理并发问题至关重要。
54 0
|
2月前
|
监控 关系型数据库 MySQL
MySQL锁机制与解决死锁问题
MySQL锁机制与解决死锁问题
267 5
|
28天前
|
关系型数据库 MySQL 数据库
mysql锁详解
通过理解并合理运用MySQL中的锁机制,开发者可以有效管理数据库并发访问,平衡性能与数据一致性需求。更多关于MySQL锁的深入探讨和最佳实践,请参考专业的数据库管理资源[[深入MySQL锁机制详解
32 0
|
2月前
|
SQL 缓存 关系型数据库
揭秘MySQL一条SQL语句的执行流程
以上步骤共同构成了MySQL处理SQL语句的完整流程,理解这一流程有助于更有效地使用MySQL数据库,优化查询性能,及时解决可能出现的性能瓶颈问题。
90 7
|
2月前
|
关系型数据库 MySQL 数据库
Mysql的锁
本文介绍了MySQL中表级锁和行级锁的区别,其中MyISAM仅支持表级锁,而InnoDB支持表级锁和行级锁,默认为行级锁。表级锁锁定整个表,实现简单,资源消耗少,但并发度低;行级锁仅锁定相关记录,减少冲突,提高并发度,但加锁开销大。此外,还介绍了共享锁和排他锁的概念及意向锁的作用。