PostgreSQL 中文参考文档
🌐 英文原版 官方 ↗

63.5. 索引唯一性检查 #

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

由于 MVCC,总是有必要允许重复条目在物理上存在于索引中:这些条目 可能指向同一个逻辑行的相继版本。 我们实际想要强制的行为是:任何 MVCC 快照都不可能包含两个具有 相等索引键的行。 这可以分解为向唯一索引插入新行时必须检查的以下几种情况:

此外,在按上述规则报告唯一性违反之前,访问方法必须重新检查被 插入行的存活状态。 如果它是已提交但已死,则不应报告违反。 (这种情况不会发生在插入由当前事务刚刚创建的行的普通场景中。 不过,在 CREATE UNIQUE INDEX CONCURRENTLY 期间可能 发生。)

我们要求索引访问方法自己应用这些测试,这意味着它必须深入堆中, 检查按索引内容显示具有重复键的任何行的提交状态。 这无疑是丑陋且非模块化的,但它节省了重复工作: 如果我们做一次单独的探测,那么为冲突行做的索引查找在寻找新行 索引条目的插入位置时基本上会被重复执行。 更何况,除非冲突检查是新索引条目插入的一个组成部分,否则没有 明显的办法避免竞态条件。

如果唯一约束是可延迟的,还存在额外的复杂性: 我们需要能够为新行插入一个索引条目,但把任何唯一性违反错误 推迟到语句结束甚至更晚。 为了避免不必要的重复索引搜索,索引访问方法应当在初始插入期间 做一次初步的唯一性检查。 如果这表明肯定没有冲突的活动元组,我们就完成了。 否则,我们安排在执行该约束的时刻进行一次重新检查。 如果在重新检查的时候,被插入的元组和某个其他具有相同键的元组 都是活动的,就必须报告错误。 (注意,就这个目的而言, “活动”实际上意味着 “索引条目的 HOT 链中的任何元组是活动的”。) 为实现这一点,aminsert 函数会收到一个具有 以下取值之一的 checkUnique 参数: