MySQL锁

全局锁,锁整个数据库

表级锁,锁表

行级锁,锁行

全局锁

全局锁就是对整个数据库实例加锁,枷锁后整个实例就处于只读状态,后续的DML的写语句,ddl语句,已经更新操作的事务提交语句都将被阻塞。

其典型的场景是做全库的逻辑备份,对所有表进行锁定,从而获取性视图,保证数据的完整性。

全局锁进行备份有以下问题:

  • 如果在主库上备份,那么在备份期间都不能执行更新,业务基本就得停摆

  • 如果在从库上备份,那么在备份期间从库不能执行主库同步过来的二进制日志,会导致主从延迟。

在InnoDB引擎中,我们可以在备份时加上参数 --single-transaction 参数来完成不加锁的一致数据备份

表级锁

分类

  • 表锁

  • 元数据锁

  • 意向锁

表锁

表共享读锁(read lock),即读锁之后,允许读,但不允许写

表独占写锁(write lock)

语法

元数据锁(meta data lock)

元数据锁的加锁过程是系统自动控制的,无需显示使用,在访问一张表的时候会自动加上。MDL锁主要作用是维护表元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。

在对一张表进行增删改查的时候,加MDL读锁(共享);当对表结构进行变更操作的时候,加MDL(排他锁)

排他锁是指和其他所有的元数据锁都不能共存,会被阻塞。

共享锁,比如当前有一个事务正在读取数据,但未提交,此时如果新开一个事务,修改某个数据,是可以的。

image-20240303154336466

意向锁

为了解决DML语句在执行时,加行锁与表锁的冲突,在InnoDB中引入了意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查。

  • 意向共享锁(IS):由语句select...lock in share mode添加。和表锁的read兼容于表锁的write互斥

  • 意向排他锁(IX):由insert,update,delete,select...for update 添加。与表锁的read和wrote互斥。

行级锁

行级锁每次锁住操作对应的行数据。发生锁冲突概率最低

InnoDB的数据是基于索引组织的,行锁是通过索引上的索引项加锁来实现的,而不是对记录加的锁。对于行级锁,主要分为以下三类:

  • 行锁(Record lock):锁定单个行记录的锁,防止其他事务对此进行update和delete,在RC,RR隔离级别下支持。(RC read commit,RR,read repeatable)

  • 间隙锁(Gap Lock):锁定索引记录间隙(不包含该记录),确保索引记录间隙不变,防止其他事务在这个间隙进行insert,产生幻读,在RR隔离级别下支持

  • 临键锁(next-key lock)。行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap。这是InnoDB在默认的RR事务隔离级别下,所使用的锁,用来防止幻读

行锁

InnoDB实现了两种类型的行锁:

  • 共享锁(S):允许一个事务去读一行,阻止其他事务相同数据的排他锁。

  • 排他锁(X):允许获取排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁

insert、update、delete会自动加排他锁

select不加任何锁

select....lock in share mode, 会加共享行锁

select...for update,会加排他行锁

注意: InnoDB默认为next-key lock

  • 针对唯一索引进行检索时,对已存在的记录进行等值匹配时,会自动优化为行锁

  • InnoDB的行锁是针对于索引加的锁,不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,此时会升级为表锁。

间隙锁/临键锁

默认情况下,InnoDB再RR事务隔离级别允许,InnoDB使用next-key锁进行搜索和索引扫描,以防止幻读。

  • 索引上的等值查询(唯一索引),给不存在的记录加锁时,优化为间隙锁

  • 索引上的等值查询(普通索引),向右遍历时,最后一个值不满足查询需求时,next-key lock 退化为间隙锁

  • 索引上的范围查询(唯一索引)会访问到不满足条件的第一个值为止。

间隙锁唯一的目的是防止其他事务插入间隙。间隙锁可以共存,一个事务采用的间隙锁不会阻止另外一个事务在同一间隙上采用间隙锁。

注:

行级锁,在InnoDB中默认增加的是,临键锁,对于查询来说,如果增加了lock in share mode,那么在不同的情况下,会对其优化为间隙锁和普通行锁。

注:

这里比较混乱,对于

这条命令的输出,不同版本的mysql可能不同。

InnoDB对于select xxx for share对普通索引的等值匹配时,首先会被查询的数据增加一个临键锁,这里锁住的是这个值的前面的间隙和这个值本身,且这个前面的间隙不好说是不是一个开区间,即,比如查age=10,在索引中age前面是1,那么在这里被锁住时,向其中插入1会失败,个人觉得是因为此时如果插入1,且在某种的排序中,这个数据在索引的B+树中应该放到了原有的1后面(比如新插入的1的id比原有1的id大),那么便无法插入。即它即将被插入到(1,10]这个间隙在里面。然后会在10的后面添加一个间隙锁这里便是上面的第二条优化原则,找到最后一个不满足的便会加入间隙锁,而非临建锁)同时如果10后面的值为13,那么此时如果插入13,且在某种排序下被插入的数据应该插入到原有的13这条数据的前面,那么依旧是插入失败。同时,如果排在了后面,那么便可以插入成功。即它不可能被插入到(10,13)这个间隙中,同时如果新插入的为10,那么显然完全不可能插入成功,无论是排序在原有10这条数据的前面or后面。

所以其实临键锁只是在间隙锁的基础上多锁住了某行数据。而且InnoDB默认会增加此锁,并在某种情况下退化。

最后更新于

这有帮助吗?