锁概述
MySQL InnoDB支持三种行锁定方式:
- 行锁(Record Lock):锁直接加在索引记录上面,锁住的是key。
- 间隙锁(Gap Lock):锁定索引记录间隙,确保索引记录的间隙不变。间隙锁是针对事务隔离级别为可重复读或以上级别而已的。可以防止幻读的发生。
- Next-Key Lock :行锁和间隙锁组合起来就叫Next-Key Lock。
默认情况下,InnoDB工作在可重复读隔离级别下,并且会以Next-Key Lock的方式对数据行进行加锁,这样可以有效防止幻读的发生。
Next-Key Lock是行锁和间隙锁的组合,当InnoDB扫描索引记录的时候,会首先对索引记录加上行锁(Record Lock),再对索引记录两边的间隙加上间隙锁(Gap Lock)。加上间隙锁之后,其他事务就不能在这个间隙修改或者插入记录。
Gap Lock在InnoDB的唯一作用就是防止其他事务的插入操作,以此防止幻读的发生。
行锁(Record Lock)
单条索引记录上加锁,record lock锁住的永远是索引,而非记录本身,即使该表上没有任何索引,那么innodb会在后台创建一个隐藏的聚集主键索引,那么锁住的就是这个隐藏的聚集主键索引。所以说当一条sql没有走任何索引时,那么将会在每一条聚集索引后面加X锁(排它锁),这个类似于表锁,但原理上和表锁应该是完全不同的。
什么时候使用行锁?
当增删改查匹配到索引时,Innodb 会使用行级锁。
如果没有匹配不到索引,那么就会直接使用表级锁。
间隙锁(Gap Lock)
在索引记录之间的间隙中加锁,或者是在某一条索引记录之前或者之后加锁,并不包括该索引记录本身。gap lock的机制主要是解决可重复读模式下的幻读问题,关于幻读的演示和gap锁如何解决了幻读。
打开间隙锁设置
首先查看 innodb_locks_unsafe_for_binlog 是否禁用:
show variables like 'innodb_locks_unsafe_for_binlog';
查看结果:
innodb_locks_unsafe_for_binlog
:默认值为OFF,即启用间隙锁。因为此参数是只读模式,如果想要禁用间隙锁,需要修改 my.cnf(windows是my.ini) 重新启动才行。
# 在 my.cnf 里面的[mysqld]添加
[mysqld]
innodb_locks_unsafe_for_binlog = 1
Next-Key Lock
间隙锁(Gap Lock)是Innodb在可重复读提交下为了解决幻读问题时引入的锁机制,幻读的问题存在是因为新增数据记录操作,这时如果进行范围查询的时候(加锁查询),会出现不一致的问题,这时使用不同的行锁已经没有办法满足要求,需要对一定范围内的数据进行加锁,间隙锁就是解决这类问题的。
临键锁的主要目的,也是为了避免幻读(Phantom Read)。如果把事务的隔离级别降级为RC,临键锁则也会失效。
在可重复读隔离级别下,数据库是通过行锁和间隙锁共同组成的(next-key lock),来实现的加锁规则有以下特性:
- 加锁的基本单位是(next-key lock),它是前开后闭原则;
- 查询过程中访问的对象会增加锁;
- 索引上的等值查询–给唯一索引加锁的时候,next-key lock升级为行锁;
- 索引上的等值查询–向右遍历时最后一个值不满足查询需求时,next-key lock 退化为间隙锁;
- 唯一索引上的范围查询会访问到不满足条件的第一个值为止。
锁的拓展
表级锁
表级锁可以分为:表锁、元数据锁、意向锁三种。
表锁
表锁,顾名思义就是对某个表加锁。
那什么时候会使用表锁呢?
- 一般情况是对应的存储引擎没有行级锁(例如:MyIASM);
因为对应存储引擎不支持行锁,所以只能是使用更粗粒度的锁来实现,这也比较好理解。 - 对应的 SQL 语句没有匹配到索引。
如果存储引擎支持行锁,但对应的 SQL 就没有使用索引,那么此时也是会全表扫描,那此时也是会使用表锁。
元数据锁
元数据,指的是我们的表结构这些元数据。元数据锁(Metadata Lock)自然是执行 DDL 表结构变更语句时,对表加上的一个锁了。
那什么时候会使用元数据锁这个表级锁呢?
- 当对一个表数据做增删改查操作的时候,会加上 MDL 读锁;
- 当我们要对表结构做变更时,就会加 MDL 写锁。
意向锁
意向锁是指,未来的某个时刻,事务可能要加共享/排它锁了,先提前声明一个意向。
InnoDB为了支持多粒度锁机制(multiple granularity locking),即允许行级锁与表级锁共存,而引入了意向锁(intention locks)。
意向锁是一个表级别的锁(table-level locking)
意向锁又分为:
- 意向共享锁(intention shared lock, IS),它预示着,事务有意向对表中的某些行加共享S锁;
select ... lock in share mode; //要设置IS锁
- 意向排它锁(intention exclusive lock, IX),它预示着,事务有意向对表中的某些行加排它X锁;
select ... for update; //要设置IX锁
但是从实现算法上来分的话,Innodb的三种行锁分别是:间隙锁、行锁和Next-Key Lock。
参考:
https://www.shuzhiduo.com/A/KE5QYmx05L/
https://www.cnblogs.com/crazylqy/p/7773492.html
https://www.jianshu.com/p/32904ee07e56
MySQL的锁机制 - 记录锁、间隙锁、临键锁
Mysql之Innodb锁机制详解
深入分析MySQL行锁加锁规则讲述数据存在/不存在情况下加锁的范围