当前位置: 首页 > article >正文

【MySQL】InnoDB存储引擎中的锁

实现事务隔离级别的过程中用到了锁,所谓锁就是在事务A修改某些数据时,对这些数据加一把锁,防止其他事务同时对这些数据执行修改操作;当事务A完成修改操作后,释放当前持有的锁,以便其他事务再次上锁执行对应的操作。不同存储引擎中的锁功能并不相同,这里重点介绍 InnoDB存储引擎中的锁


1. 锁信息

锁的信息包括锁的请求(申请),锁的持有以及阻塞状态等等,都保存在 performance_schema 库的 data_locks 表中,可以通过以下方式查看:

SELECT * FROM performance_schema.data_locks\G

 当没有开启任何事务的时候查看是没有任何信息的:

 这里我是用简单的user表来进行测试展示:

 1. 开启事务并执行锁定操作

START TRANSACTION;

执行一些操作来创建锁,例如查询或修改数据,而不提交事务,这将触发锁。

select * from users where id = 1 for update;

 2. 在另一个会话中查看锁信息

*************************** 1. row ***************************
               ENGINE: INNODB                      -- 表示存储引擎是 InnoDB
       ENGINE_LOCK_ID: 139677990608088:1084:139677906555600  
                                                      -- InnoDB 引擎中的锁标识符,通常由多个部分组成
ENGINE_TRANSACTION_ID: 4618                         -- 事务ID,表示锁属于哪个事务
            THREAD_ID: 228937                        -- 持有该锁的线程ID,可以用于跟踪哪个线程持有锁
             EVENT_ID: 23                            -- 事件ID,用于跟踪Performance Schema中的事件
        OBJECT_SCHEMA: test                          -- 数据库名称,此锁操作涉及的数据库为 "test"
          OBJECT_NAME: users                         -- 锁定的对象名称,这里是 "users" 表
       PARTITION_NAME: NULL                          -- 表分区名称,如果表有分区,显示分区名称;否则为 NULL
    SUBPARTITION_NAME: NULL                          -- 子分区名称,表无子分区则为 NULL
           INDEX_NAME: NULL                          -- 索引名称,如果锁作用在某个索引上,这里会显示索引名
OBJECT_INSTANCE_BEGIN: 139677906555600               -- 锁对象实例的内存地址,用于区分不同锁
            LOCK_TYPE: TABLE                         -- 锁的类型,这里表示锁是作用于整个表
            LOCK_MODE: IX                            -- 锁模式为 "Intent Exclusive" (IX),表示意向排他锁
          LOCK_STATUS: GRANTED                       -- 锁的状态,"GRANTED" 表示锁已授予成功
            LOCK_DATA: NULL                          -- 锁的附加数据,通常在行锁时会显示更多数据,这里为 NULL

3. 提交或回滚事务

在提交或回滚事务之后,再次查看 performance_schema.data_locks 表时,应该不再看到这个锁记录,因为锁已经被释放。

1.1 锁类型

锁类型依赖于存储引擎,在InnoDB存储引擎中按照锁的粒度分为,行级锁 RECORD和表级锁TABLE :
行级锁也叫 行锁,是对表中的某些具体的数据行进行加锁;
表级锁也叫 表锁,是对整个数据表进行加锁。

在之前版本的BDB存储引擎中还支持页级锁,锁定的是一个数据页,MySQL8中没有页级锁

 1.2 锁模式

锁模式,用来描述如何请求(申请)锁,分为共享锁(S)、独占锁(X)、意向共享锁(IS)、意向独占锁(IX)、记录锁、间隙锁、Next-Key锁、AUTO-INC 锁、空间索引的谓词锁等。

2. 共享锁和独占锁

InnoDB实现了标准的行级锁,分为两种分别是 共享锁(S锁) 独占锁(X锁) ,独占锁也称为排他锁。

  • 共享锁(S锁):允许持有该锁的事务读取表中的一行记录,同时允许其他事务在锁定行上加另一个共享锁并读取被锁定的对象,但不能对其进行写操作;
  • 独占锁(X锁):允许持有该锁的事务对数据行进行更新或删除,同时不论其他事务对锁定行进行读取或修改都不允许对锁定行进行加锁;

如果事务T1持有R行上的共享锁(S),那么事务T2请求R行上的锁时会有如下处理:

  • T2请求S锁会立即被授予,此时T1和T2都对R行持有S锁;
  • T2请求X锁不能立即被授予,阻塞到T1释放持有的锁

如果事务T1持有R行上的独占锁(X),那么T2请求R行上的任意类型锁都不能立即被授予,事务T2必须等待事务T1释放R行上的锁。

读锁是共享锁的一种实现,写锁是排他锁的一种实现。 

3. 意向锁

InnoDB 中的 意向锁(Intention Locks) 是一种元数据锁,用于协调不同事务对表级和行级锁的兼容性,确保表锁和行锁之间的并发操作不会产生冲突。意向锁不会直接锁定数据,而是用来表示某个事务将要已经在某些行上获取了特定类型的锁。

InnoDB 使用意向锁实现多粒度级别的锁,意向锁是表级别的锁,它并不是真正意义上的加锁,而只是在 data_locks 中记录事务以后要对表中的哪一行加哪种类型的锁(共享锁或排他锁),意向
锁分为两种:

  • 意向共享锁(IS,Intention Shared Lock): 表示事务打算在表的某些行上加共享锁(S 锁)。共享锁允许其他事务读取行,但不允许修改。

  • 意向排他锁(IX,Intention Exclusive Lock): 表示事务打算在表的某些行上加排他锁(X 锁)。排他锁禁止其他事务同时读取或修改被锁定的行。

当一个事务要在某个行上获取行级锁时,InnoDB 会在获取行锁之前首先在表级别上设置一个意向锁。这种意向锁的作用是声明该事务在这个表中会对某些行加锁。这样,当另一个事务想对整个表加一个表级锁(如表共享锁 S 或表排他锁 X)时,InnoDB 可以根据意向锁快速判断是否能授予表锁。举例:

  • 当事务1对表中的某行加排他锁(行 X 锁)时,系统会先在该表上设置一个意向排他锁(IX 锁)。
  • 当另一个事务2试图对整个表加表共享锁(S 锁)时,系统会检查表上的意向锁,如果发现有意向排他锁(IX),事务2的表共享锁请求会被阻塞,直到事务1释放其行锁和意向锁。

在请求锁的过程中,如果将要请求的锁与现有锁兼容,则将锁授予请求的事务,如果与现有锁冲突,则不会授予;事务将阻塞等待,直到冲突的锁被释放。意向锁的主要作用是在不同锁之间进行兼容性检测,表锁和意向锁的兼容性矩阵如下:

4. 索引记录锁

索引记录锁或称为精准行锁,顾名思意是指索引记录上的锁,如下SQL锁住的是指定的一行,例如,当你使用 SELECT FOR UPDATE 语句查询并锁定一行时,InnoDB 会对通过索引找到的记录加上排他锁(X 锁),直到事务提交或回滚时才会释放这些锁。

索引记录锁总是锁定索引行,在表没有定义索引的情况下,InnoDB创建一个隐藏的聚集索引,并使用该索引进行记录锁定,当使用索引进行查找时,锁定的只是满足条件的行,如图所示: 

5. 间隙锁

间隙锁锁定的是索引记录之间的间隙,或者第一个索引记录之前,再或者最后一个索引记录之后的
间隙。如图所示位置,根据不同的查询条件都可能会加间隙锁:

间隙锁通常用于防止幻读现象的发生,尤其是在 REPEATABLE READ 隔离级别下。

例如有如下SQL,锁定的是ID(10,20)之间的间隙,注意不包括10和20的行,目的是防止其他事务将ID值为15的列插入到列 account 表中(无论是否已经存在要插入的数据列),因为指定范围值之
间的间隙被锁定;

  • 只防插入,不防更新:间隙锁的作用是防止其他事务在锁定的“间隙”中插入新记录,但它不会阻止对现有记录的修改。换句话说,间隙锁针对的是插入操作,而不是更新操作。
  • 锁定的范围是开区间:间隙锁锁定的是索引记录之间的区域,是一个“开区间”。比如,索引记录 (3, 5) 之间的间隙是指 3 < x < 5 的所有范围。

对于使用唯一索引查询到的唯一行,不使用间隙锁,如下语句,id列有唯一的索引,只对id值为100的行使用索引记录锁:

 6. 临键锁

Next-key 锁是索引记录锁和索引记录之前间隙上间隙锁的组合,如图所示:

临键锁(Next-Key Lock) 是 InnoDB 存储引擎中的一种锁定机制,用来在高并发事务下保护数据的完整性和一致性。它结合了记录锁(Record Lock) 间隙锁(Gap Lock)的特点,既锁定了实际存在的记录,也锁定了记录之间的“间隙”,从而防止其他事务对这些范围进行插入、更新、删除等操作。

临键锁主要用于避免幻读现象,特别是在REPEATABLE READ隔离级别下,确保查询结果的一致性。

临键锁的工作方式是将索引记录及其周围的间隙一起锁定。在InnoDB中,临键锁在涉及范围查询时特别有效。假设一个表中有索引 (1, 3, 5),如果一个事务执行查询 SELECT * FROM table WHERE id > 1 FOR UPDATE,InnoDB 将会:

  • 锁定 id = 3id = 5 的索引记录(记录锁)。
  • 同时锁定 1 < id < 33 < id < 5 之间的空隙(间隙锁)。

这样做的目的是确保在该事务执行期间,其他事务无法插入 id=2id=4 这样的记录,从而避免了幻读。

7. 自增锁

AUTO-INC锁也叫自增锁是一个表级锁,服务于配置了 AUTO_INCREMENT 自增列的表。在插入数据时会在表上加自增锁,并生成自增值,同时阻塞其他的事务操作,以保证值的唯一性。需要注意的是,当一个事务执行新增操作已生成自增值,但是事务回滚了,申请到的主键值不会回退,这意味着在表中会出现自增值不连续的情况。


http://www.kler.cn/news/360774.html

相关文章:

  • 一个Docker管理工具,让您的Docker容器自动更新
  • Redis 数据类型Geospatial Indexes(地理空间索引)
  • PLC_博图系列☞基本指令”TP:启动脉冲定时器“
  • Flume面试整理-配置文件格式
  • 性能工具之 HAR 格式化转换JMeter JMX 脚本文件
  • 多一DY4100数字式接地电阻测试仪使用测量方法
  • 数据库SQL查询
  • uploads-labs靶场刷题记录
  • 如何在windows下搭建一个gitlab
  • Lua中的goto语句
  • windows系统中,在cmd窗口演练 Redis 基本操作命令
  • JavaWeb合集17-简化开发—公共字段自动填充
  • rabbitMQ的延迟队列(死信交换机)
  • 运用AI实践|如何从AI工具提升工作效率实践
  • 重生之“我打数据结构,真的假的?”--1.顺序表(无习题)
  • LCX端口转发
  • 【代码随想录——数组——二刷】
  • 一、PyCharm 基本快捷键总结
  • go生成二维码
  • OpenAi推出ChatGPT客户端