当同一个查询在不同的时间产生不同的行集时,事务中就会出现 所谓的幻象问题。例如,如果 a SELECT执行了两次,但第二次返回了第一次没有返回的行,则该行是“幻像”行。
假设表的id列上有一个索引,child并且您想要读取并锁定表中标识符值大于 100 的所有行,以便稍后更新所选行中的某些列:
SELECT * FROM child WHERE id > 100 FOR UPDATE;
查询从 id大于 100 的第一条记录开始扫描索引。让表包含id值为 90 和 102 的行。如果在扫描范围内的索引记录上设置的锁不锁定在间隙中进行的插入 (在这种情况下,90 和 102 之间的差距),另一个会话可以将新行插入到表中,其 id值为 101。如果您要 SELECT在同一事务中执行相同的操作,您将看到一个新行,其id值为 101(一个 “幻影”) 在查询返回的结果集中。如果我们把一组行看成一个数据项,那么新的幻像子就会违反一个事务应该能够运行的事务的隔离原则,使得它所读取的数据在事务过程中不会发生变化。
为防止出现幻像,请InnoDB使用一种称为下一个键锁定的算法,该算法将索引行锁定与间隙锁定相结合。 InnoDB执行行级锁定的方式是,当它搜索或扫描表索引时,它会在它遇到的索引记录上设置共享或排他锁。因此,行级锁实际上是索引记录锁。此外,索引记录上的 next-key lock 也会影响 索引记录之前的“ gap ”。也就是说,next-key 锁是索引记录锁加上索引记录前面的间隙上的间隙锁。如果一个会话记录了共享或排他锁R在索引中,另一个会话不能 R在索引顺序之前的间隙中插入新的索引记录。
在InnoDB扫描索引时,还可以锁定索引中最后一条记录之后的间隙。就在前面的例子中发生了这种情况:为了防止任何插入到 id大于 100 的表中,设置的锁 包括在值 102 InnoDB之后的间隙上的锁 。id
您可以使用 next-key 锁定在您的应用程序中实现唯一性检查:如果您在共享模式下读取数据并且没有看到要插入的行的重复项,那么您可以安全地插入您的行并知道在读取期间在您的行的后继上设置的 next-key lock 可防止任何人同时为您的行插入重复项。因此,next-key 锁定使您能够“锁定”表中不存在的东西。
可以禁用间隙锁定,如 第 15.7.1 节,“InnoDB 锁定”中所述。这可能会导致幻像问题,因为当间隙锁定被禁用时,其他会话可以将新行插入到间隙中。




