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锁包含每个维度的边界,每次就锁住这些边界就可以了。

总结

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

相关文章

  • SQL实现LeetCode(184.系里最高薪水)

    SQL实现LeetCode(184.系里最高薪水)

    这篇文章主要介绍了SQL实现LeetCode(184.系里最高薪水),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • Mysql数据库百万级数据测试索引效果

    Mysql数据库百万级数据测试索引效果

    这篇文章主要为大家介绍了Mysql数据库百万数据测试索引效果,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • MySQL索引失效的原因及实现逻辑

    MySQL索引失效的原因及实现逻辑

    在MySQL中索引失效指的是查询语句无法有效地使用索引,而必须进行全表扫描,索引失效可能会导致查询性能下降,特别是在处理大量数据时,这篇文章主要介绍了MySQL索引失效的原因及实现逻辑,需要的朋友可以参考下
    2025-12-12
  • linux 之centos7搭建mysql5.7.29的详细过程

    linux 之centos7搭建mysql5.7.29的详细过程

    这篇文章主要介绍了linux 之centos7搭建mysql5.7.29的详细过程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-05-05
  • mysql安装配置方法图文教程(CentOS7)

    mysql安装配置方法图文教程(CentOS7)

    这篇文章主要为大家详细介绍了centos7下mysql安装配置方法图文教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-04-04
  • MySQL分组查询获取每组最新的一条数据详解(group by)

    MySQL分组查询获取每组最新的一条数据详解(group by)

    在写报表功能时遇到一个需要根据用户id分组查询最新一条钱包明细数据的需求,下面这篇文章主要给大家介绍了关于MySQL分组查询获取每组最新的一条数据的相关资料,需要的朋友可以参考下
    2024-08-08
  • 重置MySQL 8.0 Root密码的简便方法小结

    重置MySQL 8.0 Root密码的简便方法小结

    这篇文章主要给大家介绍了几种轻松重置 MySQL 8.0 Root 密码的简便方法,文中通过代码示例讲解的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-10-10
  • MySQL实战教程之Join语句执行流程

    MySQL实战教程之Join语句执行流程

    这篇文章主要介绍了MySQL Join语句执行流程,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-03-03
  • MySQL常见优化方案汇总

    MySQL常见优化方案汇总

    mysql数据库是中小微企业常用的一种数据化管理工具,它具有轻便,简洁,免费等特点。今天通过本文给大家介绍MySQL常见优化方案汇总,感兴趣的朋友一起看看吧
    2022-01-01
  • MySQL复制架构的搭建及配置过程

    MySQL复制架构的搭建及配置过程

    这篇文章主要介绍了MySQL复制架构的相关知识,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-02-02

最新评论