MySQL中InnoDB的锁分类

栏目: 数据库 · 发布时间: 5年前

内容简介:文章翻译自Innodb实现了标准的行级锁,包括共享锁和排它锁两种类型如果事务T1拥有行r上的共享锁,那么对于不同的事务T2的对行r上的请求,有以下处理方式:

文章翻译自 15.7.1 InnoDB Locking ,其中所有锁特性都以mysql-8.0版本为准,其中包含的锁种类如下:

  • 共享锁和排它锁
  • 意向锁
  • 记录锁
  • 间隙锁
  • 后键锁
  • 插入意向锁
  • 自增锁
  • 空间索引的谓词锁

共享锁和排它锁(Shared and Exclusive Locks)

Innodb实现了标准的行级锁,包括共享锁和排它锁两种类型

  • 共享锁(s锁)允许拥有该锁的事务读取该行的记录
  • 排它锁(x锁)允许拥有该锁的事务更新或删除该行的记录

如果事务T1拥有行r上的共享锁,那么对于不同的事务T2的对行r上的请求,有以下处理方式:

  • 可以立即授予T2共享锁,最终T1和T2都拥有共享锁
  • 不能立即授予T2排它锁

如果事务T1拥有行r上的排它锁,则对于不同的事务T2,不能授予其行r上的任何锁,T2必须等待T1释放其对行r的锁定

意向锁(Intention Locks)

InnoDB支持多种粒度的锁定,来允许行锁和表锁同时存在,例如 LOCK TABLE ... WRITE 语句可以在指定的表上使用排它锁。为了实现多粒度锁定,InnoDB使用了意向锁

意向锁是表级锁,它表示一个事务稍后对表中的行需要使用的锁类型,意向锁有两种类型:

  • 意向共享锁(IS锁)表示一个事务倾向于对表中的部分行设置共享锁
  • 意向排它锁(IX锁)表示一个事务倾向于对表中的部分行设置排它锁

例如, SELECT ... FOR SHARE 会设置IS锁, SELECT ... FOR UPDATE 会设置IX锁

意向锁的协议如下:

  • 在事务可以获取表中某行的共享锁之前,必须先在表上获取IS锁,或表上更强的锁
  • 在事务可以获取表中某些行的排它锁之前,必须先在表上获取IX锁

表级锁的兼容性通过以下的矩阵进行了总结:

MySQL中InnoDB的锁分类

如果请求事务与当前存在的锁兼容,则授予锁。如果冲突则不会授予,事务会进行等待,直到冲突的锁被释放。永远不会在冲突情况下授予锁,因为会导致数据库的死锁

意向锁不会阻塞除全表请求外的任何内容(如 LOCK TABLES ... WRITE ),意向锁的主要目的是显示某人正在或正要锁定表中的行

意向锁锁定的事务数据在 SHOW ENGINE INNODB STATUS 和InnoDB监视器中输出的内容大致是以下这种形式:

TABLE LOCK table `test`.`t` trx id 10080 lock mode IX
复制代码

记录锁(Record Locks)

记录锁,是对索引记录的锁定。例如, SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; 会阻止其他事务的插入、更新,以及删除t表中c1=10的行

即使表中没有定义索引,记录锁也始终会锁定索引记录。在这种情况下,InnoDB会创建一个隐藏的聚集索引,并使用此索引来进行记录锁定

记录锁锁定的事务数据在 SHOW ENGINE INNODB STATUS 和InnoDB监视器中输出的内容大致是以下这种形式:

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        ;;
复制代码

间隙锁(Gap Locks)

间隙锁,锁定的是索引记录之间的间隙,或是第一个索引之前以及最后一个索引之后的间隙,如 SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE; 会阻止其他事务将值15插入到t.c1中,无论该列是否存在任何这样的值,因为该范围内的任何存在的值都会被锁定

这个间隙可能会跨越单个索引、多个索引,甚至是空

间隙锁是性能和并发能力之间的一些权衡,仅会作用于某些事务隔离级别

使用唯一索引来搜索唯一行的语句不会使用间隙锁(不包括搜索条件仅包含多列唯一索引的某些列的情况,在这种情况下,还是会使用间隙锁),比如,如果id列使用了唯一索引,则以下语句仅使用对于id为100的锁使用索引记录锁,而不关心其他的会话是否在前一个间隙中插入行:

SELECT * FROM child WHERE id = 100;
复制代码

如果id列没有索引,或使用的是非唯一索引,则该语句会锁定前一个间隙

值得注意的是,互相冲突的锁可以通过不同的事务在间隙上保持,例如,事务A在间隙上保持共享的间隙锁(间隙S锁),事务B可以在同一间隙上保持独占的间隙锁(间隙X锁)。允许互相冲突的间隙锁的原因是,如果从索引中清除记录,则必须合并由不同事务保留在记录上的间隙锁

InnoDB的间隙锁是“纯粹的抑制”,这意味着间隙锁的唯一目的是阻止其他事务插入间隙。间隙锁可以共存,一个事务的间隙锁不会阻止另一个事务在同一个间隙上进行间隙锁的锁定,共享间隙锁和排他间隙锁之间没有区别,彼此不冲突,它们拥有相同的功能

可以通过明确的声明来禁用间隙锁,比如将事务隔离级别设置为 READ COMMITTED 。在这种情况下,对于搜索和索引扫描会禁用间隙锁,仅会作用于外键约束和重复键的检查

使用 READ COMMITTED 隔离级别还会有其他影响,MySQL评估 WHERE 条件后,会释放不匹配行的记录锁。对于 UPDATE 语句,InnoDB执行“半一致”读取,使之能将最新的版本提交给MySQL,MySQL就可以确定该行是否与 UPDATEWHERE 条件匹配

后键锁(Next-Key Locks)

后键锁,是索引记录上的 记录锁 和索引记录之前的 间隙锁 的组合

InnoDB使用这样一种方式来进行行级锁定:当搜索或扫描表索引时,会在遇到的索引记录上设置共享锁或排它锁,因此,行级锁实际上是索引记录锁

索引记录上的后键锁也会影响该索引记录之前的“间隙”,也就是说后键锁是索引记录锁加上该索引记录之前的间隙上的间隙锁。如果一个会话在索引中的记录R上有共享锁或排它锁,则另一个会话不能在索引顺序中的R之前的间隙中插入新的索引记录

假设索引中包含值10、11、13,和20,此索引可能的后键锁会覆盖以下键值,其中圆括号表示排除的间隔端点,方括号表示包含的端点(即开集和闭集的概念):

(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
复制代码

对于最后一个间隔,后键锁将间隙锁定在索引中最大值之上,且伪记录“supermum”的值高于索引中任何的实际值,supermun不是真正的索引记录,因此,实际上这个后键锁(指最后一个间隙的后键锁)仅锁定最大索引值之后的间隙

默认情况下,InnoDB在 REPEATABLE READ 隔离级别下运行,在这种情况下,InnoDB使用后键锁进行搜索和索引扫描,从而防止幻读

后键锁的事务数据在 SHOW ENGINE INNODB STATUS 和InnoDB监视器中输出的内容大致是以下这种形式:

RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t` 
trx id 10080 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

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        ;;
复制代码

插入意向锁(Insert Intention Locks)

插入意向锁,是在行插入之前由 INSERT 操作设置的一种间隙锁。该锁表示以这种方式插入的意图:如果插入到相同索引间隙中的多个事务,不插入间隙内的相同位置,则不需要等待彼此

假设存在值为4和7的索引记录,两个单独的事务分别尝试插入值为5和6的记录,在获取插入行的排他锁之前,分别使用插入意向锁来锁定4到7之间的间隙,但是不会相互阻塞,因为行为是不冲突的

以下示例演示了在获取插入记录的排它锁之前,使用插入意向锁来进行锁定。该示例涉及两个客户端,分别是A和B

客户端A创建了一个包含两个索引记录(99和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);
复制代码

插入意向锁的事务数据在 SHOW ENGINE INNODB STATUS 和InnoDB监视器中输出的内容大致是以下这种形式:

RECORD LOCKS space id 31 page no 3 n bits 72 index `PRIMARY` of table `test`.`child`
trx id 8731 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80000066; asc    f;;
 1: len 6; hex 000000002215; asc     " ;;
 2: len 7; hex 9000000172011c; asc     r  ;;...
复制代码

自增锁(AUTO-INC Locks)

自增锁,是由插入到具有 AUTO_INCREMENT 自增属性的列中的事务锁采用的特殊表级锁。在最简单的情况下,如果一个事务正在向表中插入值,则其他任何事务都必须等待该表执行自己的插入操作,以便被第一个事务插入的行接收到连续的主键值

innodb_autoinc_lock_mode 配置选项用来控制用于自增锁定的算法,它允许您在可预测的自增值序列和插入操作的最大并发量之间权衡

空间索引的谓词锁(Predicate Locks for Spatial Indexes)

InnoDB支持包含空间性列的 SPATIAL 索引(参考 11.5.9 Optimizing Spatial Analysis

要处理涉及 SPATIAL索引 的操作的锁定,后键锁不能很好地支持 REPEATABLE READSERIALIZABLE 事务隔离级别,多维数据中没有绝对有序的概念,因此不能准确地判断哪一个是“后键”

为了支持包含 SPATIAL索引 的表的隔离级别,InnoDB使用了 谓词锁SPATIAL索引 包含最小边界矩形(MBR)值,因此InnoDB在用于查询的MBR值上设置谓词锁来强制对索引进行一致性读取,其他事务无法插入或修改与查询条件匹配的行


以上所述就是小编给大家介绍的《MySQL中InnoDB的锁分类》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

海量运维、运营规划之道

海量运维、运营规划之道

唐文 / 电子工业出版社 / 2014-1-1 / 59.00

《海量运维、运营规划之道》作者具有腾讯、百度等中国一线互联网公司多年从业经历,书中依托工作实践,以互联网海量产品质量、效率、成本为核心,从规划、速度、监控、告警、安全、管理、流程、预案、考核、设备、带宽等方面,结合大量案例与读者分享了作者对互联网海量运维、运营规划的体会。 《海量运维、运营规划之道》全面介绍大型互联网公司运维工作所涉及的各个方面,是每个互联网运维工程师、架构师、管理人员不可或......一起来看看 《海量运维、运营规划之道》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

SHA 加密
SHA 加密

SHA 加密工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具