9.3 9.4 9.5 9.6 10 11 12 13 14
阿里云PostgreSQL 问题报告 纠错本页面

61.5. 索引唯一性检查

PostgreSQL使用唯一索引来强制 SQL 唯一性约束,唯一 索引实际上是不允许多个项有相同键的索引。一个支持这个特性的访问方法要 设置amcanunique为真(目前,只有 b-tree 支持它)。在强制唯一性时不会考虑INCLUDE子句中列出的列。

因为 MVCC,必须允许重复的项在物理上存在于索引之中:这些项可能指向某个单一逻辑行的后继版本。实际想强制的行为是,任何 MVCC 快照都不能包含两个具有相同索引键的行。在向一个唯一索引中插入一个新行时需要被检查的情况可分解成:

此外,在根据上述规则报告唯一性违背之前,访问方法必须重新检查刚被插入的行的存活性。如果已经因为事务的提交而死亡,那么不应当报告任何违背(这种情况不可能出现在插入在同一事务中创建的行的普通场景中。但是在CREATE UNIQUE INDEX CONCURRENTLY的过程中是可能发生的)。

要求索引访问方法自己应用这些测试,这就意味着它必须到达堆来查看那些根据 索引内容有重复键的任意行的提交状态。这无疑是丑陋并且非模块化的,但是这样可以节约重复的工作:如果我们进行一次独立的探测,那么查找一个冲突行的索引查找本质上将在查找插入新行索引项位置时被重复。此外,没有很明显的方法来避免竞争情况,除非冲突检查是插入新索引项动作的一部分。

如果唯一约束是可延迟的,就存在额外的复杂性:我们需要能够为一个新行插入一个索引项,但是推迟任何唯一性违背错误直到语句结束或者更晚。为了避免对索引不必要的重复搜索,索引访问方法应该在初始插入过程中做一次初步的唯一性检查。如果显示绝对不会有冲突的活元组,就可以完成。否则,我们计划一次重新检查,它将在强制约束的时候发生。在重新检查时,如果具有相同键的被插入元组和某个其他元组都活着,则必须报告错误(注意为了这个目的,“活着”实际意味着“在索引项的 HOT 链上的任何元组都活着”)。要实现这一点,需要给 aminsert 传递一个 checkUnique 参数,其中包含下列值之一: