间隙锁是对索引记录之间的间隙的锁,或者对第一条索引记录之前或之后的间隙的锁。例如,SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;
防止其他事务在t.c1列中插入值15,无论该列中是否已经存在任何此类值,因为范围中所有现有值之间的间隙都被锁定。
间隙可能跨越单个索引值、多个索引值,甚至可能为空。
间隙锁是性能和并发性之间权衡的一部分,用于某些事务隔离级别,而不是其他级别。
对于使用唯一索引锁定行以搜索唯一行的语句,不需要使用间隙锁定。
(这不包括搜索条件只包含多列唯一索引的某些列的情况;在这种情况下,会发生间隙锁定。)
例如,如果ID列具有唯一索引,则以下语句只对ID值为100的行使用索引记录锁,而不管其他事务是否插入前一间隙中的行:
SELECT * FROM child WHERE id = 100;
如果ID没有被索引或具有非唯一索引,那么语句将锁定前面的间隙。
这里也值得注意的是,冲突的锁可以通过不同的事务保持在间隙上。( It is also worth noting here that conflicting locks can be held on a gap by different transactions.)例如,事务A可以在间隙上持有共享间隙锁(gap S-lock),而事务B可以在同一间隙上持有独占间隙锁(gap X-lock)。允许间隙锁冲突的原因是,如果从索引中清除某个记录,则必须合并由不同事务保留在该记录上的间隙锁。
InnoDB中的间隙锁是“完全禁止的(purely inhibitive)”,这意味着它们的唯一目的是防止其他事务插入到间隙中。间隙锁可以共存。一个事务获取的间隙锁不会阻止另一个事务在同一间隙上获取间隙锁。共享和独占的间隙锁没有区别。它们不会相互冲突,并且执行相同的功能。
可以显式禁用间隙锁定。如果将事务隔离级别更改为“读取已提交(READ COMMITTED)”,则会发生这种情况。在这些情况下,搜索和索引扫描将禁用间隙锁定,并且仅用于外键约束检查和重复键检查。
使用 READ COMMITTED 隔离级别还有其他影响。不匹配行的记录锁在 MySQL 计算 WHERE 条件后释放。对于 UPDATE 语句,InnoDB 执行“半一致”读取,这样它会将最新提交的版本返回到MySQL,以便MySQL可以确定该行是否与 UPDATE 的WHERE 条件匹配。