InnoDB中的锁

2021.03.29 04:03 133
阅读约 5 分钟

本节将介绍InnoDB中所使用的锁的类型。

  • 共享和互斥锁
  • 意向锁
  • 记录锁
  • 间隙锁
  • next-key
  • 插入意向锁
  • 自增锁
  • 空间索引的Predicate Locks

共享与互斥锁—行级锁

InnoDB引擎通过共享(s)锁与互斥(x)锁实现行级锁的功能。

  • 持有共享锁的事务允许读取一行数据
  • 持有互斥锁的事务允许更新或删除一行数据

如果事务T1持有行r的共享锁,那么来自事务T2对该行的锁的获取操作有如下两种:

  • T2获取共享锁:可以立即获得,此时事务T1T2都持有行r的s锁。
  • T2获取互斥锁:获取操作会被阻塞直到T1释放锁。

如果事务T1持有的是互斥锁,那么不管T2获取任意锁的操作都会被阻塞。

意向锁—表级锁

InnoDB支持多粒度锁,允许行锁和表锁共存。例如LOCK TABLES … WRITE语句会在指定的表上获取互斥锁。为了在多个粒度级别上实现锁,InnoDB使用了意向锁,意向锁是表级别的锁,它指示事务稍后需要对表中的一行使用哪种类型的锁(共享锁还是排他锁)。意向锁有两种形式:

  • 意向共享锁(IS)表示接下来要对表中单独的行设置共享锁。
  • 意向互斥锁(IX)表示接下来要对表中单独的行设置共享锁。

例如,SELECT … FOR SHARE 将会设置意向共享锁。SELECT … FOR UPDATE 会设置意向互斥锁。

意向锁定协议如下:

  • 在事务获得表中某一行的s前,它必须首先获得表上的IS锁。
  • 在事务获得表中某一行的x之前,它必须首先获得表上的IX锁。

表级别的锁的关系如下表所示:

 XIXSIS
X冲突冲突冲突冲突
IX冲突兼容冲突兼容
S冲突冲突兼容兼容
IS冲突兼容兼容兼容

如果一个锁与现有的锁兼容,那么就可以获取到到锁。一个事务会一直等待冲突的锁释放。

意向锁不会阻塞除全表请求之外的任何请求(例如,LOCK TABLE …  WRITE)。意向锁的主要目的是显示某人正在锁定某一行,或者将要锁定表中的某一行。

记录锁—行级锁

记录锁锁定的是该行的索引记录。例如SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;会阻塞其它事务的插入、更新、删除该行的操作。

记录锁总是会锁住索引记录,即使表中没有明确的定义索引。在这种情况下,InnoDB引擎会创建一个隐藏的聚集索引并是将该索引作为记录锁,详系内容见“聚集索引和二级索引“。

在SHOW ENGINE INNODB STATUS和INNODB monitor输出中,记录锁的事务数据类似如下:

RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t`
trx id 10078 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;
 1: len 6; hex 00000000274f; asc     'O;;
 2: len 7; hex b60000019d0110; asc        ;;

间隙锁

间隙锁锁定的是记录锁之间的数据,并且锁定第一个索引记录之前或最后一个索引记录之后的空隙。

例如,SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE; 会阻止其他事务将数据插入到这个范围内,不管这个范围内是否已经存在这条数据记录

间隙可能跨越单个索引值、多个索引值,甚至是空的。

间隙锁是性能与一致性之间的权衡,在一些隔离级别中可能使用了间隙锁,而另一些没有。

对于使用唯一索引锁定行以搜索唯一行的语句,不需要间隙锁定。(这不包括搜索条件只包括多列唯一索引的一些列的情况;在这种情况下,间隙锁定会发生。)例如,如果id列有一个唯一的索引,下面的语句只对id值为100的行使用index-record锁,而不管其他会话是否在前面的间隙插入行:

SELECT * FROM child WHERE id = 100;

如果id没有被索引,或者有一个非唯一的索引,那么该语句将锁定前面的间隙。

这里还值得注意的是,不同的事务可以在一个间隙上持有冲突的锁。例如,事务A可以在一个间隙上持有一个共享间隙锁(gap S-lock),而事务B在同一个间隙上持有一个排他间隙锁(gap X-lock)。允许冲突间隙锁的原因是,如果一条记录从一个索引中被清除,那么记录上由不同事务持有的间隙锁必须被合并。

InnoDB中的Gap锁是“纯抑制的”,这意味着它们的唯一目的是防止其他事务插入Gap。间隙锁可以共存。一个事务取得的间隙锁并不会阻止另一个事务取得同一间隙上的间隙锁。共享和互斥间隙锁之间没有区别。它们彼此之间不冲突,并且执行相同的功能。

使用READ COMMITTED隔离级别还会产生其他影响。不匹配行的记录锁在MySQL计算WHERE条件后释放。对于UPDATE语句,InnoDB会做一个“半一致”的读取,它会返回最新提交的版本给MySQL,这样MySQL就可以确定这一行是否匹配UPDATE的WHERE条件。

Next-key Locks 

Next-key locks 是将记录锁与间隙锁相结合的一种锁。InnoDB执行行级锁的方式是这样的:当它搜索或扫描一个表索引时,它会在遇到的索引记录上设置共享锁或排他锁。因此,行级锁实际上是索引记录锁。

如果一个会话在一个索引中的记录R上有一个共享锁或排他锁,另一个会话不能在紧接在索引顺序中的R之前的间隙中插入新的索引记录。

假设一个索引包含值10、11、13和20。该索引可能的next-key锁覆盖以下区间,圆括号表示排除区间,方括号表示包含端点:

(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)

默认情况下,InnoDB操作在REPEATABLE READ事务隔离级别。在这种情况下,InnoDB使用next-key锁进行搜索和索引扫描,这样可以防止幻读。

自增锁

当向包含有AUTO_INCREMENT 的列的表插入数据时,事务会获得该锁。如果一个事务在向表中插入数据,其它事务必须等待该操作完成,以便在之前自增的序列的基础上继续自增操作。

 

字数:1185 发布于 6 个月前
Copyright 2018-2021 Siques