### 15.7.4幻影行

[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)

当同一个查询在不同的时间生成不同的行集时，事务中就会出现所谓的幻象问题。例如，如果[`选择`](select.html)执行两次，但第二次返回第一次未返回的行，该行是“幻影”行。

假设有一个索引在`身份证件`专栏`小孩`表中的所有行，并希望从标识符值大于100的表中读取和锁定所有行，以便稍后更新所选行中的某些列：

```
SELECT * FROM child WHERE id > 100 FOR UPDATE;
```

查询从第一条记录开始扫描索引，其中`身份证件`大于100。让表包含具有`身份证件`值为90和102。如果在扫描范围内的索引记录上设置的锁没有锁定在间隙中进行的插入（在本例中，间隙在90和102之间），则另一个会话可以使用`身份证件`101人。如果你执行同样的命令[`选择`](select.html)在同一事务中，您将看到一个新行`身份证件`查询返回的结果集中的101（一个“幻影”）。如果我们将一组行视为一个数据项，那么新的幻影子级将违反事务的隔离原则，即事务应该能够运行，以便它读取的数据在事务期间不会更改。

为了防止幽灵，`InnoDB`使用名为next key locking的算法，该算法将索引行锁定与间隙锁定相结合。`InnoDB`执行行级锁定的方式是，当它搜索或扫描表索引时，会对遇到的索引记录设置共享或排他锁。因此，行级锁实际上是索引记录锁。此外，索引记录上的下一个键锁也会影响索引记录之前的“间隔”。也就是说，下一个密钥锁是索引记录锁加上索引记录前面的间隙锁。如果一个会话有共享或独占锁定记录`R`在索引中，另一个会话不能在紧接之前的间隙中插入新的索引记录`R`按索引顺序排列。

什么时候`InnoDB`扫描索引时，它还可以锁定索引中最后一条记录之后的间隔。在前面的例子中就是这样：为了防止在`身份证件`会大于100，锁由`InnoDB`在下面的间隙处加锁`身份证件`价值102。

您可以使用下一个密钥锁定在应用程序中实现唯一性检查：如果您在共享模式下读取数据，并且没有看到要插入的行的重复项，那么您可以安全地插入行，并知道在读取过程中对行的后续行设置的下一个密钥锁定可以防止任何人同时为您的行插入重复项。因此，下一个键锁定使您能够“锁定”表中不存在的内容。

如中所述，可以禁用间隙锁定[第15.7.1节“InnoDB锁定”](innodb-locking.html)。这可能会导致幻象问题，因为当禁用间隙锁定时，其他会话可以将新行插入间隙。
