InnoDB 锁定和事务模型

熟悉InnoDB的锁定和事务模型,对于实现高流量,高并发和高可靠的应用程序和调整MySQL性能是非常重要的。

1.InnoDB 锁类型

  • 共享锁和排他锁(Shared and Exclusive Locks)
  • 意向锁(Intention Locks)
  • 记录锁(Record Locks)
  • 间隙锁(Gap Locks)
  • 下一键锁(Next-Key Locks)
  • 插入意向锁(Insert Intention Locks)

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

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

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

排他(X)锁允许持有该锁的事务更新或删除行。

如果事务T1在行r上持有一个共享(S)锁,那么来自某些不同事务T2的对行r上的锁的请求将按以下方式处理:

T2对S锁的请求可以立即获得批准。 结果,T1和T2都在r上保持了S锁。

T2不能立即授予X锁请求。

如果事务T1在行r上持有排他(X)锁,则不能立即批准某个不同事务T2对r上任一类型的锁的请求。 相反,事务T2必须等待事务T1释放对行r的锁定。

意向锁(Intention Locks)

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

意向共享锁(IS)表示事务打算对表中的各个行设置共享锁。

意向排他锁(IX)表示事务打算对表中的各个行设置排他锁。

例如,SELECT ... LOCK IN SHARE MODE设置IS锁定,而SELECT ... FOR UPDATE设置IX锁定。

意向锁定协议如下:

在事务可以获取表中某行的共享锁之前,它必须首先获取该表中的IS锁或更强的锁。

在事务可以获取表中某行的排他锁之前,它必须首先获取该表中的IX锁。

表级锁类型(X,S表示锁定发生在表不是上面内容的行)的兼容性汇总在以下矩阵:

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

如果一个事务要请求的锁与事务之前已持有的锁兼容,则事务将获取到请求的锁。如果与事务之前已持有的锁冲突的话,则事务必须等待已持有的冲突锁释放后才能获取请求的锁。

如果新请求的锁与已持有的锁冲突,并且获取新请求的锁可能会导致死锁的话,InnoDB会报错。

意向锁只阻塞全表请求(例如LOCK TABLES...WRITE)。 意向锁的主要目的是表明某人正在锁定表中的行或要锁定表中的行。

事务获取行锁之前,先用意向锁检测表锁是否冲突。如果不冲突的话,继续获取行锁操作。

记录锁(Record Locks)

记录锁是对索引记录的锁定。 例如,SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; 防止任何其他事务插入,更新或删除t.c1值为10的行。

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

间隙锁(Gap Locks)

间隙锁根据查询条件分三种情况锁定 :

1. (id > A and id <b)   对索引记录之间的间隙进行锁定

2. (id <A) 第一个索引记录之前的间隙进行锁定 

3.(id  >B) 最后一个索引记录之后的间隙进行锁定。

例如,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或启用innodb_locks_unsafe_for_binlog系统变量(现已弃用),则会发生这种情况。 在这种情况下,将禁用间隙锁定来进行搜索和索引扫描,并且间隙锁定仅用于外键约束检查和重复键检查。

使用READ COMMITTED隔离级别或启用innodb_locks_unsafe_for_binlog还有其他效果。 MySQL评估WHERE条件后,将释放不匹配行的记录锁。

具体间隙锁的使用例子可以参考:https://www.cnblogs.com/crazylqy/p/7821481.html

下一键锁(Next-Key Locks)

InnoDB执行行级锁定的方式是,当它搜索或扫描表索引时,会在查找到的索引记录上设置共享或互斥锁。因此,行级锁实际上是索引记录锁

下一键锁是索引记录锁定加上索引记录之前的间隙锁的组合。 如果一个会话在索引中的记录R上具有共享或排他锁,则另一会话无法在索引查找开始位置之后和索引记录R之前的间隙中

插入新的索引记录。

假定索引包含值10、11、13和20。此索引可能的下一键锁涵盖以下间隔(其中,圆括号表示排除区间端点,方括号表示包括端点):

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

对于最后一个时间间隔,此下一键锁仅锁定跟随最大索引值的间隙。

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

插入意向锁(Insert Intention Locks)

插入意向锁是一种在行插入之前通过INSERT操作设置的间隙锁。此锁以以下方式发出信号来表明插入意图:如果多个事务未插入间隙中的相同位置,则无需等待彼此插入的多个事务。假设有索引记录,其值分别为4和7。单独的事务分别尝试插入值5和6,在获得插入行的排他锁之前,每个事务都使用插入意向锁来锁定4和7之间的间隙,但不要互相阻塞,因为行是无冲突的。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值