【译文】MySQL InnoDB 使用的锁分析

InnoDB 使用的 锁类型

  共享锁和排它锁

  意向锁

  记录锁

  间隙锁

  Next-key 锁

  插入意向锁

  AUTO-INC 锁

共享锁和排他锁

  InnoDB实现了俩个标准的行级锁,共享锁和排它锁。

    共享锁允许持有者读取一行

    排它锁允许持有者更新或者删除一行

  如果一个事物T1在 r 行持有一把共享锁,则来自于不同的事物T2在 r 行上的锁请求将按照如下方法处理:

    T2请求共享锁可以立即被授权,因此T1和T2都持有了在 r 行上的共享锁

    T2请求排它锁不可以立即被授权。

  如果一个事物T1在 r 行持有一把排它锁,则来自与不同事物T2在r上的任何锁请求将不会立即被授权。

意向锁

  InnoDB支持多粒度锁:指允许表级锁和行级锁共存。为了实现在多粒度级别上锁,额外的锁类型(意向锁)被使用。意向锁是一个表级锁,它表明未来在那张表上会有哪一种锁类型(排他或者共享)被事物请求。

  InnoDB有俩种类型的意向锁(假设事物将来会在某张表t上请求锁):意向共享锁,意向排他锁。

  意向共享锁:指事物意图在表t上的某些行上加S锁

  意向排它锁:指事物意图在表t上的某些行上加X锁

  意向锁协议描述如下:

    事物在表t上的一行上加S锁之前,他必须获取IS锁或者在t上更强的锁(指表级锁)

    事物在表t上的一行上加X锁之前,他必须获取IX锁

  上述锁规则协议可以方便的概括如下的兼容性矩阵:

  X IX S IS
X Conflict Conflict Conflict Conflict
IX Conflict Compatible Conflict Compatible
S Conflict Conflict Compatible Compatible
IS Conflict Compatible Compatible Compatible

    注:意向锁是MySQL自己自动处理,为了解决表级锁和行级锁的冲突,即提早检测锁冲突。

S、X、IS、IX锁兼容性矩阵为什么是这样子呢?

1、意向锁之间彼此不会冲突,因为它们都只是“有意”,而不是真干,所以是可以兼容的。在加行锁之前,会使用意向锁判断是否冲突;
2、IX和X的关系等同于X和X之间的关系,为什么呢?因为事务获得了IX锁,接下来就有权利获取X锁,这样就会出现两个事务都获取X锁的情况,这和我们已知的X锁和X锁之间互斥是矛盾的;
3、S和IS、X和IS、IX和IS也可以由此推导出来。

  一个锁被授予一个事物,是在该锁与事物已获取的锁兼容的时候,而不是与现有锁冲突的时候。事物等待直到冲突的锁被释放。如果一个锁请求与现有的锁冲突,并且因为死锁不能被授予,一个错误会发生,MySQL自己决定哪个事物回滚

  因此意向锁不阻塞任何事物,除了全表锁请求。IX锁和IS锁是用来表明某个人或者事物正在持有锁或者将要持有锁。

 

记录锁

  一个记录锁是在索引记录上的锁,仅锁住一行。例如:SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; 阻止任何其他事物插入、更新、删除c1=10的行,前提是c1有索引。

  记录锁锁住的只是记录的索引“记录”,及时一个表没有定义任何索引,InnoDB也会默认创建一个聚簇索引,并用来加锁。

  不符合where条件的记录锁会被释放。

间隙锁

  间隙锁是指锁住索引记录之间的空隙(并不锁住索引记录,即开区间),或者明确指定索引记录的第一个和最后一个,例如:SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE; 阻止任何其他事物的插入、更新和删除

  Gap可以仅有一个值,多个值或者为空。

  Gap锁是在性能和并发度上权衡的结果,并被某些事物隔离级别使用。

  Gap锁在一些使用唯一索引的来检索唯一行的语句中是没有必要的,因为索引的结果是唯一的,没有间隙,不会妨碍其他会话(事物)插入记录。注,那些语句中包含唯一索引和二级索引的仍然适用Gap锁。

  值得注意的是,在不同事物中,锁冲突可以发生在同一个间隙里,例如一个事物A持有一把共享GAP锁在Gap间隙中,同时事物B持有一把排他的Gap锁,俩个Gap属于同一个。

    注,举一个例子,假设数据库存在id为1,2,3,5,7;事物1:select * from student where id between 3 and 9 for update; 事物2:select * from student where id = 8; 俩个事物共享(7-9)这个间隙锁,但互不影响。

    冲突Gap被允许的原因是如果一个记录从索引中被清除了,不同事物在这个记录上的Gap锁必须合并。

    注:上述情况未实验成功,首先是不太明白其意思,其次是如果删除索引记录,意味着加X锁,然而这个锁的排他性已经是冲突了,并不能验证成功。如果有人直销烦请告知,不胜感激!

  Gap锁在InnoDB中是纯抑制作用,意思是Gap锁仅阻止其他事物插入这个gap间隙,他们不阻止不同的事物在相同的gap上获取gap锁。因此,GapX锁和GapS锁有相同的效果。

  Gap锁可以被禁用,通过设置RC隔离级别,或者启用 innodb_locks_unsafe_for_binlog系统变量(当前已标记为了弃用),此时,在检索和扫描的时候Gap锁被禁用,且仅被用作外键检查和重复值检测。

注:当用唯一索引时,三种方法可能产生间隙锁:

  3. 检索值和比较操作符,例如大于小于检索值(此时符合的记录也会加记录锁,不仅仅是间隙锁)

  

Next-key Locks

  Next-key锁是记录锁和Gap锁的结合。

  InnoDB 执行行级锁,因此,当检索或扫描表索引时,他设置S或者X锁到遇到的索引记录上。因此,行级锁实际上是记录锁,一个索引记录上的next-key锁,同样影响索引记录之前的gap。就是说next-key锁是一个记录锁,加上一个gap(记录之前)锁。

  假设索引里面有10,11,13,20。则可能的next-key锁是:

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

  默认地,InnoDB提供的事物隔离级别是RR,此时InnoDB使用的是next-key锁,在检索和扫描的时候。以阻止幻读。next-key在使用唯一索引时会降级为记录锁,提高并发度。

插入意向锁(一种特殊的gap锁):

  插入意向锁是当插入操作执行时,设置的一种特殊的gap类型锁。这个lock标志一种插入行为:如果每个事物插入gap中的位置不一样时在多事物插入相同的gap情况不需要互相等待。这主要是提高并发插入效率。

AUTO-INC锁

  该锁类型是一种特殊的表级锁,在事物插入自动增长字段时使用。如果一个事物插入列值(自动增长列)到表中,任何其他的事物必须等待,因此插入的字段才会是连续的值。

   innodb_autoinc_lock_mode配置选项,控制自动增长锁使用的算法,它允许你选择插入操作时,如何来权衡可预测的自动增长值和最大并发度

注:

  排它锁和共享锁是指锁的性质,意图锁是意图。

  记录锁和Gap锁是锁的范围,这就会出现是在一定的范围加特定性质的锁的问题。