龙空技术网

MySQL InnoDB的七种锁,你都了解吗?

易善知 1170

前言:

现在同学们对“mysqlinnodb锁表”大约比较讲究,看官们都想要剖析一些“mysqlinnodb锁表”的相关内容。那么小编也在网络上收集了一些有关“mysqlinnodb锁表””的相关文章,希望你们能喜欢,你们一起来了解一下吧!

本文介绍InnoDB包含的锁的种类:共享锁(Shared Lock)和 排他锁(Exclusive Lock)意向锁(Intention Locks)Record LocksGap Locks 间隙锁Next-Key LocksInsert Intention LocksAUTO-INC Locks共享锁和排他锁

InnoDB实现了两种类型的行级锁:shared(S) locks、exclusive(X) locks。

共享锁(S):允许事务读取一行数据排他锁(X):允许事务更新或删除一行数据

如果事务T1在记录r上持有共享锁,那么其他事务T2对记录r的锁请求处理方式如下:

事务T2可以立即获得请求的S锁。结果是T1和T2在记录r上都获得了S锁。事务T2不能立即获得请求的X锁。

如果事务T1在行r上获得了X锁,事务T2在行r上请求的任何一种类型的锁都不能立即获得。反而,事务T2不得不等待T1释放在行r上的X锁。

意向锁(Intention Locks)

InnoDB支持多粒度(multiple granularity)锁定,这种锁定允许行级锁和表级锁共存。为了在实际中支持多粒度加锁操作,InnoDB使用了一种附加类型的锁,称为意向锁。在InnoDB中意向锁是表级锁。

InnoDB使用了两种类型的意向锁(假设事务T已请求表t上指定类型的锁):

意向共享锁(IS): 事务T打算获得表t上某几行的共享锁。意向排他锁(IX): 事务T打算获得表t上某几行的排他锁。

例如,SELECT ... LOCK IN SHARE MODE会请求IS锁,SELECT ... FOR UPDATE会请求IX锁。

意向锁的协议如下:

一个事务在表的某一行上获得S锁之前,它必须先在这个表上获得IS锁或者更强的锁。一个事务在表的某一行上获得X所之前,它必须先在这个表上获得IX锁。

这些规则可以总结如下:

如果事务请求的锁跟已经存在的锁兼容,则事务会得到它请求的锁,若跟已存在的锁有冲突,则不会获得它请求的锁。事务会一直等待直到存在冲突的锁被释放。如果一个锁请求跟已存在的锁有冲突并且不能获得,则会产生死锁。

Record Locks

Record Lock是一个在索引记录上的锁。例如,SELECT c1 FOR UPDATE FROM t WHERE c1 = 10; 防止其他事务在行t.c1 = 10上的插入、删除和更新操作。

Record locks总是锁定索引记录,即使表中没有一个索引。这种情况下,InnoDB会使用隐式的聚簇索引来进行锁定。

间隙锁(Gap Locks)

间隙锁是在索引记录之间的间隙上的锁,或者是在第一个索引记录之前或最后一个索引记录之后的间隙上的锁。例如,SELECT c1 FOR UPDATE FROM t WHERE c1 BETWEEN 10 and 20; 可以阻止其他事务插入t.c1 = 15的记录,不论列中是否已有此值,因为在此范围内的所有现有值之间的间隙被锁定了。

间隙可能会跨越单个索引值,多个索引值,甚至空值。

当使用唯一索引查询唯一行时不需要使用间隙锁。(这不包含此种情况,查询条件只包含多列唯一索引的一部分字段;这种情况下还是会用到间隙锁。)例如,如果id列上有唯一索引,下面的语句仅对id等于100的行使用索引记录锁,不会关心其他会话是否在间隙之前插入行:

若id字段未加索引或者为非唯一索引,此语句会锁住id=100记录前面的间隙。

值得注意的是通过不同的事务相互冲突的锁可以持有同一个间隙。例如,事务A在一个间隙上持有共享间隙锁(gap S-lock),同时事务B可以在此间隙上持有排他间隙锁(gap X-lock)。这种情况被允许的原因是:如果一个记录从索引上被清除,则此记录上被不同事务持有的间隙锁必须合并。

在InnoDB中,间隙锁是“完全被抑制(purely inhibitive)”的,也就是说它只会阻止其他事务在间隙中进行插入操作。它不会阻止不同的事务在同一间隙上获取间隙锁。因此,排他间隙锁跟共享间隙锁具有相同的效果。

当事务隔离级别设置为 READ COMMITTED 或者 启用系统变量innodb_locks_unsafe_for_binlog时,间隙锁可以被显示禁用。在这种情况下,间隙锁在查询和索引扫描时会被禁用,仅在外键约束检查和唯一性检查时启用。

当事务隔离级别设置为READ COMMITTED或者启用系统变量innodb_locks_unsafe_for_binlog时还有其他效果。MySQL在评估完WHERE条件后针对不匹配的行会释放记录锁。对于UPDATE语句,InnoDB做了半一致性(semi-consistent)读,它会返回最后提交的版本到MySQL,以便MySQL可以决定这行是否跟UPDATE语句的WHERE条件匹配。

Next-Key Locks

Next-Key Lock是索引记录上的record lock与索引记录之前间隙上的gap lock的组合。

当InnoDB搜索或扫描表的索引时会使用行级锁,会在它遇到的索引记录上设置共享锁或排他锁。因此,行级锁就是索引记录锁。索引记录上的next-key lock也会对此索引记录之前的间隙产生影响。也就是说,next-key lock是索引记录锁加上该索引记录之前间隙上的间隙锁。如果一个会话在索引的记录R上持有一个共享锁或排他锁,那么另一个会话不能在R之前的间隙上立即插入一个新的索引记录。

假设一个索引包含10,11,13和20这四个值,那么该索引的next-key locks可能覆盖如下区间:

对于最后一个区间,next-key lock锁住的是索引中大于最大值的间隙(锁住的仅仅是索引中最大值之后的间隙)。上确界是一个大于索引中任何一个记录的伪记录。上确界并不是一个真实的索引记录,因此,事实上next-key lock仅仅锁住的是最大索引值之后的间隙。

默认情况下,InnoDB运行在REPEATABLE READ事务隔离级别并且系统变量innodb_locks_unsafe_for_binlog 未启用。在这种情况下,InnoDB使用next-key lock搜索和扫描索引,避免了幻影行(phantom rows)。

Insert Intention Locks

插入意向锁是在插入行之前通过INSERT操作设置的一种间隙锁。这个锁发出以如下方式插入的信号:如果多个事务在同一索引间隙上的不同位置进行插入时,他们并不需要彼此相互等待。假设有值为4和7的索引记录,两个事务尝试分别插入5和6,在获得要插入行上的排他锁之前,每个都用插入意向锁锁住4和7之间的间隙,但是不会相互阻塞,因为插入的行不存在冲突。

下面的例子演示了事务在获得插入记录上的排他锁之前先获得插入意向锁。这个例子包含两个客户端,A和B。

客户端A创建了一个包含两个索引记录(90和102)的表,然后开启了事务,在id值大于100的索引记录上设置了排他锁。这个排他锁包含记录102之前的间隙锁:

客户端B开启事务后,往这个间隙中插入记录。这个事务持有插入意向锁,并等待获得排他锁。

通过运行SHOW ENGINE INNODB STATUS来查看插入意向锁的信息,TRANSACTIONS标题下的信息如下:

AUTO-INC Locks

自增长锁是一个特殊的表级锁,事务用此锁为AUTO_INCREMENT字段插入数据。最简单的情况下,如果一个事务正在往表中插入数据,那么另一个事务的插入操作必须等待,以至于第一个事务的插入操作能够获得连续的主键值。

innodb_autoinc_lock_mode配置选项能够控制自增长锁的算法。它可以让你在自增长值的可预测序列和最大并发插入操作之间做权衡。

分享自:

标签: #mysqlinnodb锁表 #mysql innodb锁