14.7.4 幻影行

当同一查询在不同时间产生不同的行集时,在事务内就会发生所谓的幻象问题。例如,如果SELECT被执行两次,但是第二次返回的行而不是第一次返回,则该行是“幻像”行。

假设childtable 的id列上有一个索引,并且您想要读取和锁定该 table 中标识符值大于 100 的所有行,目的是稍后更新所选行中的某些列:

SELECT * FROM child WHERE id > 100 FOR UPDATE;

该查询从id大于 100 的第一条记录开始扫描索引。让 table 包含id值分别为 90 和 102 的行。如果在扫描范围内对索引记录设置的锁定不锁定在间隔(在本例中为 90 和 102 之间的间隔),另一个会话可以将新行插入id为 101 的 table 中。如果您要在同一事务中执行相同的SELECT,则会看到新行查询返回的结果集中的id为 101(“幻影”)。如果我们将一组行视为数据项,则新的幻影子级将违反事务应能够运行的事务隔离原则,以使已读取的数据在事务期间不会更改。

为了防止产生幻像,InnoDB使用了称为下一键锁定的算法,该算法将索引行锁定与间隙锁定结合在一起。 InnoDB执行行级锁定的方式是,当它搜索或扫描 table 索引时,会在遇到的索引记录上设置共享或互斥锁。因此,行级锁实际上是索引记录锁。此外,索引记录上的下一键锁定也会影响该索引记录之前的“间隙”。即,下一键锁定是索引记录锁定加上索引记录之前的间隙上的间隙锁定。如果一个会话在索引中的记录R上具有共享或排他锁,则另一会话不能按索引 Sequences 在R之前的间隙中插入新的索引记录。

InnoDB扫描索引时,它也可以锁定索引中最后一条记录之后的间隔。前面的示例中正是这种情况:为了防止在 table 中插入id大于 100 的 table,由InnoDB设置的锁定包括对id值 102 之后的间隙的锁定。

您可以使用下一键锁定在应用程序中实施唯一性检查:如果您以共享模式读取数据,但没有看到要插入的行的重复项,则可以安全地插入行并知道在读取期间在行的后继上设置的 next 键锁可防止任何人同时为行插入副本。因此,下一键锁定使您可以“锁定”table 中不存在的内容。

第 14.7.1 节“ InnoDB 锁定”中所述,可以禁用间隙锁定。这可能会导致幻影问题,因为在禁用间隙锁定时,其他会话可以在间隙中插入新行。