MySQL-InnoDB常用锁类型解析

Shared(乐观锁) and Exclusive Locks(互斥锁):

        InnoDB有两种锁类型,Shared(s) and Exclusive(x) Locks(乐观锁和互斥锁)。

        Shared(s)Locks:允许持有该锁的事务读取数据;

        Exclusive(x) Locks:允许持有锁的事务插入,更新或修改数据;

如果事务T1在r记录上持有s锁,这个时候事务T2申请对r加锁,锁的处理方式有以下两种:

  1.      如果T2申请获取r记录的s锁,T2会立即获取s锁,这时T1和T2都有r记录的s锁;
  2.      如果T2申请获取r记录的x锁,此时T2会等待获取x锁;

注意:如果T1持有r记录的x锁,这时T2事务申请r记录任何类型(包括s锁和x锁)的锁都需要等待T1释放r记录的x锁。

 

Record Locks

记录锁作用于索引记录(行)上。例如:SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;任何事务要求对c1 = 10这条记录进行插入,更新或删除,都会立即阻塞。

record lock只会对索引记录进行加锁(index record),即使表中不存在任何索引(包括primary key)。这时MySQL会默认的给table创建聚合锁(clustered index),并且给行记录进行加锁的时候会使用它。

当使用 SHOW ENGINE INNODB STATUS语句时会展示如下信息

MySQL-InnoDB常用锁类型解析_第1张图片

请注意这句:locks rec but not gap; 证明这是Record locks 而不是 gap locks

 

Gap Locks

间隙锁,间隙锁作用于索引行区间,要么作用于索引行之前的数据或索引行后的数据。举个例子:SELECT a1 FROM t WHERE a1 BETWEEN 1 and 10 FOR UPDATE;不管在t.a1这一列上是否存在5这个值,都会阻止其他事务将5这个值插入到t.a1这一列,因为在1到10这个范围的所有行,都已经被加锁。

gap适用于单列索引,多列索引或者NULL值列。

Gap Locks在性能和并发上做出了平衡,在有些事务隔离级别(transaction isolation level)(例如REPEATABLE_READ)上起作用,在另一些上不起作用(例如READ_COMMITED)。

Gap Locks不适用于唯一索引(unique index),在唯一索引记录上,只会使用index-record lock。例如下面的语句:

SELECT * FROM T1 WHERE id = 10;如果id这一列添加了唯一索引,这种情况下,只会对id = 10这条记录使用index-record lock;如果id未使用索引或者加了非唯一索引,则会将id = 10前面的gap(间隙)加锁。

不同事务可以在同一个gap(间隙)中持有互不兼容的锁,这不足为奇。例如事务A在C-gap中持有S锁,同时事务B在C-gap中持有X锁。原因是如果某一行记录被排除在索引范围外,不同事物对相同记录持有的间隙锁必须合并。

InnoDB里面的Gap Locks仅仅是为了防止其他事务在同一个gap中进行插入(insert).Gap Locks可以并存,意味着不同事务可以在同一个gap中进行加锁而互不影响,不论是S Gap Locks和X Gap Locks。

当事务隔离级别是READ_COMMITED或者innodb_locks_unsafe_for_binlog 是启用状态时,Gap Locks可以被禁用。这种情况下,Gap Locks仅仅用来做外键约束检查或重复键检查。同时,在Mysql评估WHERE后面的查询条件后,不匹配的记录中的锁会被立即释放(REPEATABLE_READ隔离级别下,不匹配的记录也会加锁,直到所有记录遍历完毕)。对于UPDATE语句,Mysql会执行semi-consistent read(我的理解是半一致读),这种查询会返回最新提交的数据库记录,用来检查加锁记录是否满足UPDATE语句中的WHERE条件。

 

Next-Key Locks

next-key lock将index-record lock和gap lock结合起来,其中gap lock对index-record前的记录进行加锁。如果一个会话在索引记录R上持有X锁或者S锁,此时不允许其他会话在R记录前的索引记录中(也就是R记录前的gap中)插入数据。

假设一个索引包含值10,11,13,20。这个索引上有可能使用到的next-key lock包含下面几个区间,括号代表开区间,中括号代表闭区间:

       (negative infinity, 10]

       (10, 11]

       (11, 13]

       (13, 20]

       (20, positive infinity)

在最后一个区间中,next-key lock对大于20的值进行加锁,supremum(最小确定边界)的值大于索引中实际上的最大值(我理解此处是21)。最小确定边界不是真实的索引记录,此时gap的范围为大于20的任意值。

InnoDB默认的事务隔离级别为REPEATABLE_READ,此时使用next-key lock可以防止Phantom Rows(也许翻译为幽灵记录好理解些)的发生。

使用 SHOW ENGINE INNODB STATUS查询,在控制台中会显示以下信息:

MySQL-InnoDB常用锁类型解析_第2张图片

 

Insert Intention Locks

插入意向锁是gap locks的一种,在插入行记录的时候会在等待X锁前使用该锁。此种锁会通知其他事务当前事务在gap中的插入位置,这样如果多个事务在同一个gap中插入记录,但是它们插入的位置不同(例如id = 1, id = 2,gap[1-8])时,事务间不需要互相等待,即可直接插入。

假设有这样一张A表,id这一列为主键,该表目前存在记录(1,2,3,6),如下图:

MySQL-InnoDB常用锁类型解析_第3张图片

这时,客户端A执行下图的语句:

START TRANSACTION;

SELECT * FROM t_trx_test2 WHERE id > 3 FOR UPDATE;

客户端B执行下图语句:

START TRANSACTION;

INSERT INTO t_trx_test2 (id, name, code) VALUES (4, '4', '4')

使用SHOW ENGINE INNODB STATUS便会得到如下结果:

LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 3047, OS thread handle 139674939606784, query id 462305 223.73.227.90 l8dev update
INSERT INTO t_trx_test2 (id, name, code) VALUES (4, '4', '4')
------- TRX HAS BEEN WAITING 12 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 476 page no 3 n bits 80 index PRIMARY of table `jboot-b2c`.`t_trx_test2` trx id 167318 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 5 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
 0: len 4; hex 80000006; asc     ;;
 1: len 6; hex 000000026b0b; asc     k ;;
 2: len 7; hex bb000001310110; asc     1  ;;
 3: len 1; hex 36; asc 6;;
 4: len 1; hex 36; asc 6;;
 5: len 1; hex 36; asc 6;;

 

AUTO-INC LOCKS

AUTO-INC LOCKS是一种表级别锁,当一个表格中存在AUTO_INCREMENT列,事务尝试在该表中插入记录时,会使用该中特殊的表级别锁。最简单的场景是,当A事务试图在该表中插入记录时,其他试图在该表中插入记录的事务必须等待A事务,以使A事务插入的记录获得连续的主键。

 innodb_autoinc_lock_mode这个设置控制了Mysql使用何种算法进行auto-increment lock,在AUTO_INCREMENT列上插入时,让你在列最大并发插入的性能和获取连续可预测的序列上做出取舍。

 

你可能感兴趣的:(mysql)