MySql InnoDB引擎下的锁使用及说明

 更新时间:2025年12月30日 15:00:38   作者:鲜虾云吞爱好者  
文章介绍了MySQL InnoDB存储引擎中的锁机制,包括共享锁、排他锁、意向锁、记录锁、间隙锁、Next-Key锁、插入意向锁、自增锁以及空间索引的谓词锁,这些锁机制确保了数据的一致性和并发控制

MySql锁种类

在innodb中,锁的种类如下:

  • Shared and Exclusive Locks(共享锁和排他锁)
  • Intention Locks(意向锁)
  • Record Locks(记录锁)
  • Gap Locks(间隙锁)
  • Next-key Locks(Gap Lock + Record Lock)
  • Insert Intention Locks(插入意向锁)
  • AUTO-INC Locks(自增锁)
  • Predicate Locks for Spatial Indexes

Shared and Exclusive Locks

Innodb实现了标准的行级锁,其中有两种类型:

  • 共享锁:允许持有共享锁的事务读取该行记录。
  • 排他锁:允许持有排他锁的事务更新或者删除该行记录。

如果事务T1持有某行数据的共享锁,那么另一个事务T1想获取该行数据的锁时:

  • 共享锁:T2会马上获得一个共享锁,此时T1、T2都有该行数据的共享锁。
  • 排他锁:T2不能马上获得排他锁。

如果事务T1持有某行数据的排他锁,那么其他事务都不能立刻获得该行数据的共享锁或排他锁。其他事务需要等待T1释放它所持有的该行数据的排他锁。

Intention Locks

Innodb支持多粒度锁,允许行锁和表锁共存。为了实现多粒度级别的锁,innodb使用了意图锁。意图锁是表级别的锁,它指示事务稍后需要表中的一行使用哪种类型的锁(共享或排他)。

以下是两种类型的意向锁:

  • 共享意向锁:表明该事务计划对某行数据设置一个共享锁。
  • 排他意向锁:表明该事务计划对某行数据设置一个排他锁。

例如,select .... for share就设置了一个共享意向锁,select .... for update就设置了一个排他意向锁。

意向锁的形为:

  • 在一个事务获取到某行锁的共享锁之前,它必须先获得一个共享意向锁或者更高级别的锁(也就是排他意向锁啦)。
  • 在一个事务获取到某行锁的排他锁之前,它必须先获得一个排他意向锁。

表级锁类型的兼容性如下:

排他锁

排他意向锁

共享锁

共享意向锁

排他锁

冲突

冲突

冲突

冲突

排他意向锁

冲突

兼容

冲突

兼容

共享锁

冲突

冲突

兼容

兼容

共享意向锁

冲突

兼容

兼容

兼容

事务可以获得与已经存在的锁兼容的锁,不能获得与已经存在的锁冲突的锁。在有冲突的锁被释放之前,事务会一直等待。如果一个锁的请求与已存在的锁冲突并且因为有可能产生死锁而不能被获得,那么就会产生一个错误。

除了全表请求(如,lock table ... write)之外,意向锁不会阻塞任何事物。意向锁的主要目的就是表面有人正在对某行进行加锁,或者正要锁住表内某行。

Record Locks

记录锁是对索引进行记录的锁。例如,select c1 from t where c1 = 10 for update 禁止了任何其他事务对c1 = 10的数据行进行插入,更新或者删除。

记录锁总是对索引进行加锁,即使该表没有定义索引。如果表没有自己定义索引,那么innodb就会定义一个隐式的聚集索引(hidden clustered index)并且对这个索引进行加锁。

Gap Locks(为了防止幻读)

间隙锁是对一个索引间隙加锁的锁。如果索引间隙的范围为(index1,index2),那么就锁住index1到index2之间的间隙,如果索引只有一个index,那么根据innoddb底层的B+树的结构,假设index前面最近的一个索引为index1,后面最近一个索引为index2,那么就会锁住index1到index2之间的间隙。

例如,select c1 from t where c1 between 10 and 20 for update 禁止了其他任何事物插入一行c1=15的数据行,无论是不是已经有了一行c1=15的数据行,因为10-20这个间隙的值都被锁上了间隙锁。间隙可能会包含单个索引值或者多个索引值,甚至有可能是空的。

再如,假设表的数据如下:

SELECT * FROM t;
+------+
| age |
+------+
| 21 |
| 25 |
| 30 |
+------+

那么,当执行select age from t where age = 25 for updata时,锁住的就是21-30之间的间隙。

间隙锁是在性能和并发之间权衡的一部分,在某些事务隔离级别会使用,在某些事务隔离级别不使用。

对于使用唯一性索引搜索唯一行的情况,不需要加上间隙锁(这不包括使用联合唯一索引而只搜索其中一部分的列数据的情况,在这种情况下确实会加上一个间隙锁)。

例如,select * from t where id = 100 只会对id值为100的行使用一个记录锁(record lock),不管其他事务是否会在前面的间隙插入行。但如果id上没有一个唯一索引,那么就会锁住某个间隙。

innodb中的间隙锁是“纯粹抑制的”(purely inhibitive),这意味着他们的唯一目的是防止其他事务插入到间隙中。间隙锁可以共存,一个事务使用间隙锁并不会阻止另一个事务使用同一间隙上的间隙锁。

可以通过将事务隔离级别设置为READ COMMITTED来禁用间隙锁。在READ COMMITTED隔离级别下,间隙锁在搜索和索引扫描中被禁用,只用于外键约束检查和重复键检查。

Next-Key Locks

Next-Key Lock是索引上的记录锁和锁住该索引前面的间隙的间隙锁的组合。

innodb通过对搜索和扫描索引时得到的索引加共享锁或排他锁来实现行级锁。因此,行级锁实际上是索引记录锁。索引记录上的next-key锁也会影响该索引记录之前的间隙。也就是说,next-key锁是由索引记录锁加上对索引之前的间隙加锁的间隙锁组成的。如果一个会话对索引中的记录R有一个共享锁或排他锁,那么另一个会话不能再紧挨着索引顺序的R之前的间隙插入一个新的索引记录。

默认情况下,innodb引擎采取REPEATABLE READ事务隔离级别,此时,innodb使用next-key lock来防止幻读。

Insert Intention Locks

插入意向锁时一种由insert操作在行插入之前的间隙锁类型。这个锁表面了插入的意图,如果插入到同一个索引间隙中的多个事务没有插入到间隙中的相同位置,那么它们就不需要互相等待。假设有值为4和7的索引,有两个事务分别像插入5和6,那么他们在插入数据之前都会锁住4和7之间的间隙,但此时他们不会互相阻塞,而他们随后会获得5和6的排他锁,因为是行锁,所有依旧不会互相阻塞。

例如:

客户端A创建一个包含两个索引记录(90和102)的表,然后启动一个事务,对ID大于100的索引记录获取排他锁。排他锁包括记录102之前的间隙锁::

mysql> CREATE TABLE child (id int(11) NOT NULL, PRIMARY KEY(id)) ENGINE=InnoDB;
mysql> INSERT INTO child (id) values (90),(102);

mysql> START TRANSACTION;
mysql> SELECT * FROM child WHERE id > 100 FOR UPDATE;
+-----+
| id  |
+-----+
| 102 |
+-----+

客户端B开始一个事务,向间隙中插入一条记录。事务在等待获得排他锁时接受一个插入意图锁:

mysql> START TRANSACTION;
mysql> INSERT INTO child (id) VALUES (101);

在客户端A的事务提交之前,客户端B的对应间隙的插入都不能被执行。

AUTO-INC Locks

AUTO-INC是一种特殊的表锁,当事务想插入一条自增行的时候使用。

例如,如果一个事务正在插入数据,任何其他事务如果也要插入,那么他们都必须等待当前事务插入完毕,这样才能保证那个自增字段是连续的。

Predicate Locks for Spatial Indexes

InnoDB支持包含空间数据的列的空间索引。为了处理想要锁住空间索引的锁,next-key锁无法实现REPEATABLE READ 或 SERIALIZABLE事务隔离级别的要求。多维数据没有绝对的排序概念,因此不知道哪一个是下一个索引,也就无法获得间隙锁。

为了支持具有空间索引的表的隔离级别,InnoDB使用Predicate Locks。一个Predicate锁包含每个维度的边界,每次就锁住这些边界就可以了。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 一文理解MySQL数据库的约束与表的设计

    一文理解MySQL数据库的约束与表的设计

    约束是用来限制表中的数据长什么样子的,即什么样的数据可以插入到表中,什么样的数据插入不到表中,下面这篇文章主要给大家介绍了关于如何通过一文理解MySQL数据库的约束与表的设计的相关资料,需要的朋友可以参考下
    2022-11-11
  • MySQL 根据多字段查询重复数据的示例代码

    MySQL 根据多字段查询重复数据的示例代码

    本文介绍了如何使用 MySQL 根据多个字段查询重复数据,我们介绍了如何根据多个字段查询重复数据,并提供了相应的代码示例,通过这些方法,我们可以快速准确地找到和处理重复数据,提高数据库的数据质量,需要的朋友可以参考下
    2023-11-11
  • 了解MySQL之Adaptive Hash Index的使用

    了解MySQL之Adaptive Hash Index的使用

    InnoDB的自适应哈希索引(AHI)是内存结构,自动优化等值查询,不支持排序,通过缓存热点数据提升效率,参数可调整,适用于特定场景
    2025-08-08
  • MySQL存储引擎InnoDB架构原理和执行流程

    MySQL存储引擎InnoDB架构原理和执行流程

    InnoDB是MySQL的当前默认存储引擎,支持外键、行级锁定和ACID事务,通过BufferPool缓冲池缓存数据,RedoLogBuffer和undo日志文件保证数据的持久性和回滚能力,MySQL宕机重启时,InnoDB会根据LSN值决定是否需要从redo日志恢复数据
    2025-04-04
  • MYSQL性能优化分享(分库分表)

    MYSQL性能优化分享(分库分表)

    MYSQL性能优化之分库分表与不停机修改mysql表结构,需要的朋友可以参考下
    2012-02-02
  • MySQL8.0.28安装教程详细图解(windows 64位)

    MySQL8.0.28安装教程详细图解(windows 64位)

    如果电脑上已经有MySQL数据库再进行重做往往会遇到问题,下面这篇文章主要给大家介绍了关于windows 64位系统下MySQL8.0.28安装教程的详细教程,文章通过图文介绍的非常详细,需要的朋友可以参考下
    2023-04-04
  • MySQL中Stmt 预处理提高效率问题的小研究

    MySQL中Stmt 预处理提高效率问题的小研究

    在oracle数据库中,有一个变量绑定的用法,很多人都比较熟悉,可以调高数据库效率,应对高并发等,好吧,这其中并不包括我,当同事问我MySQL中有没有类似的写法时,我是很茫然的,于是就上网查,找到了如下一种写法
    2011-08-08
  • Mysql using使用详解

    Mysql using使用详解

    本文主要介绍了Mysql using使用详解,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • MySQL底层数据结构选用B+树的原因

    MySQL底层数据结构选用B+树的原因

    大家好,本篇文章主要讲的是MySQL底层数据结构选用B+树的原因,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • MySQL8.0 索引优化invisible index详情

    MySQL8.0 索引优化invisible index详情

    这篇文章主要介绍了MySQL8.0 索引优化invisible index详情,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-09-09

最新评论