innodb 锁

5 篇文章 0 订阅

本文基于mysql version 5.7

innodb的锁

(1)共享锁和排他锁 Shared and Exclusive Locks

InnoDB实现标准的行级锁,其中有两种类型的锁: 共享锁(S)和排他锁(X)。

1、共享锁(S)允许持有该锁的事务进行行读取。

2、排他锁(X)(独占锁)允许持有该锁的事务更新(Update)或删除(Delete)行。
举例:
如果事务(T1)事先持有某一行(r)的共享锁,紧接着另一个事务(T2)也请求行(r)的锁,将会按以下方式处理:

  • 如果T2请求的是一个共享锁,就能立刻得到该锁,这样一来T1和T2就都持有r的共享锁。
  • 如果T2请求的是排他锁,就不能立即拿到。

如果T1事先持有的是r的排他锁,那么T2不管请求什么锁,都得等着T1释放后才能获取。

(2)意向锁(意图锁)Intention Locks

InnoDB支持多种粒度锁定,允许行锁和表锁并存。为了使在多个粒度级别上的锁定变得切实可行,InnoDB支持意图锁。意向锁是表级锁,表示事务稍后对表中的行需要哪种类型的锁(共享锁或排他锁)。
有两种类型的意图锁:

  • 意图共享锁(IS):表示有个事务打算在表中可用的行上加一个共享锁。
  • 意图排他锁(IX):表示有个事务打算在表中可用的行上加一个排他锁。

当一个事务想要获取表中某一行的共享锁时,必须先获取这张表的意图共享锁或这关于张表的级别更强的锁;当一个事务想要获取表中某一行的排他锁时,必须先获取整张表的意图排它锁。

处理多事务过程中,各种表级锁的兼容性为:

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

当需要获取的锁和表上已经存在的锁兼容时,事务才能获取锁。否则只能等待表上已经存在的锁被释放。
如果锁定请求与现有锁定发生冲突,并且由于可能导致死锁而无法被授予许可 ,则会发生错误。

意图锁不会阻塞除全表请求(例如LOCK TABLES … WRITE)以外的任何内容。
意向锁的主要目的是告知请求的事务有其他事务正在锁定表中的行,或者打算锁定表中的行。

理解意图锁,首先确认下面的概念:

  • 表锁的级别高于行锁
  • InnoDB支持多粒度锁共存

假设事务A获得了表table1的行r的排他锁,此时事务B想要申请table1的表级别共享锁,如果没有意图锁的概念,事务B获取表锁前需要做两件事:

  • 查看表table1是否已经存在更高级别的表锁
  • 查看表的每一行是否已经存在更高级别的行锁。(如果表共享锁与行独占锁同时存在,那么某一行可能被某一事务修改)

但是,如果有了意图锁,事务A在获得行r的排他锁的同时,还自动获得了表table1的意图排他锁,事务B在执行上述过程就变为:

  • 查看表table1是否已经存在更高级别的表锁
  • 查看表table1是否已经存在有冲突的意图锁
  • 涉及具体行的时候,是否存在冲突的行锁

省去了遍历所有行的锁。意图锁是与行锁相伴的一种表锁,是事务在获取行锁之前innodb帮事务额外获取的表锁,没有意图锁的话,多粒度锁的共存效率会降低。

(3)记录锁 Record Locks

记录锁是对索引记录的锁定,而不是真正的数据记录。例如, SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; 可以防止从插入,更新或删除行,其中的值的任何其它交易t.c1是 10。

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

  • 锁的是非主键,会在索引记录上加锁后,再去主键上加锁
  • 表上没有索引,会在隐藏的聚集索引上加锁
  • 如果要锁的列没有索引,进行全表记录加锁

题外介绍——聚集索引

  • 当存在主键时,会使用主键作为聚集索引
  • 没有主键时,使用第一个非空的唯一索引作为聚集索引
  • 没有主键和合适的唯一索引时,innodb会生成一列隐藏的六位单调递增的聚集索引——GEN_CLUST_INDEX
(4)间隙锁 Gap Locks

间隙锁是对索引记录之间的间隙的锁定,或者是对第一个索引之前或最后一个索引之后的间隙的锁定。例如,SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;可以防止其他事务将15插入column中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可以确定该记录是否与UPDATE的WHERE 条件匹配。

(5)临键锁 Next-Key Locks

next-key lock是索引上的记录锁和索引之前的间隙上的间隙锁的组合。

InnoDB执行行级锁,以使其在搜索或扫描表索引时对遇到的索引记录设置共享或排他锁。因此,行级锁实际上是索引记录锁(index-record locks)。索引记录上的next-key lock也会影响该索引记录之前的“ 间隙 ”。next-key lock是索引记录锁加上索引记录之前的间隙上的间隙锁。如果一个会话(session)在记录(record)R的索引上具有共享或排他锁 ,则另一会话不能在R之前的间隙中插入索引记录。

假定索引包含值10、11、13和20。此索引可能的next-key locks涵盖以下间隔,其中,圆括号表示开,方括号表示闭:

(negative infinity(负无穷), 10] ->左开右闭,下同
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity) -> 左右都开

对于最后一个间隔,next-key lock仅锁定最大索引值之后的间隙。

默认情况下,InnoDB采用的事务隔离级别是 REPEATABLE READ(可重复读)。在这种情况下,InnoDB使用next-key lock进行搜索和索引扫描,以防止产生幻读

题外介绍——幻读

幻读现象指的是同一个事务用相同的查询条件在多次查询时得到不同的查询结果。
举例:
假设在child表的id列有索引,当需要用下面的查询语句来读取并锁定id大于100的索引,并准备在稍后的操作中更新部分列:

SELECT * FROM child WHERE id > 100 FOR UPDATE;

假设这张表目前的id值有90和102两个值,这个查询操作将从id大于100的第一条记录处开始扫描索引。如果仅用记录锁来锁住索引记录,而不锁住他们之间的间隙(90-102),那么另一个会话就能够插入一条新的记录(例如id=101),这样产生的幻读行会破坏事务一致性原则。
为了避免幻读行,innodb采用了next-key lock,他由索引的记录锁和间隙锁构成。当innodb扫描索引时,它会给遇到的索引加上共享锁或排它锁,这种行级锁实际上就是记录锁。next-key lock会在扫描到的索引与上一条索引之间加上间隙锁。当扫描到最后一处索引时,innodb同样会在最后一条索引之后加上间隙锁。
在这里插入图片描述

(6)插入意图锁 Insert Intention Locks

插入意图锁定是在执行INSERT语句插入行之前设置的一种间隙锁 。当有事务即将要执行插入操作时会获取该锁,这个锁可以确保不同的事务在同一个间隙的不同位置插入时不会被阻塞
举例:
(1)
假设有索引记录4和7。两个不同的事务分别尝试插入值5和6,在获得插入行的排他锁之前,每个事务都先使用插入意图锁来锁定4和7之间的间隙,由于要插入的行不冲突,所以这两个事务相互之间不会阻塞。
(2)
下面的示例演示了一个事务在获取插入记录的排他锁之前, 先获取意图插入锁。该示例涉及两个客户端A和B。

客户端A创建一个包含两个索引记录(90和102)的表,然后启动一个事务,该事务在ID大于100的索引记录上设置排他锁(记录锁+间隙锁,为90-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开始事务后想要在间隙(90-102)处插入一条记录。事务在等待获取插入记录的排他锁时先要获取插入意图锁。

mysql> START TRANSACTION;
mysql> INSERT INTO child (id) VALUES (101);

个人理解:间隙锁是为了防止插入,插入意图锁是为了指明要在这个间隙中插入记录。

  • 间隙锁之间兼容
  • 插入意图锁在不同的插入记录处兼容
(7)自增锁 AUTO-INC Locks

当事务给一个表插入自增列时( AUTO_INCREMENT columns),会用到这个特殊的表级锁。在最简单的情况下,如果一个事务正在向表中插入值,则任何其他想做插值的事务都必须等待,以便第一个事务插入的行能接收连续的主键值。

配置项innodb_autoinc_lock_mode 用于控制自增的算法。它允许您选择如何在可预测的自序列与插入操作的最大并发性之间进行权衡。

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

(至今没遇到过,先做记录日后再看)
InnoDB支持SPATIAL 包含空间列的列的索引。

要处理涉及SPATIAL索引的操作的锁定 ,next-key lock不能很好地支持支持REPEATABLE READ或 SERIALIZABLE事务隔离级别。多维数据中没有绝对排序概念,因此不清楚哪个是 “ next-key ”。

要支持具有SPATIAL索引的表的隔离级别 ,请InnoDB 使用谓词锁。甲SPATIAL索引包含最小外接矩形(MBR)值,因此, InnoDB通过设置用于查询的MBR值的谓词锁强制上的索引一致的读取。其他事务不能插入或修改将匹配查询条件的行。
本文参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值