PostgreSQL 使用唯一索引来强制 SQL 唯一约束,唯一索引实际上是不允许多条记录有相同键值的的索引。一个支持这个特性的访问方法要设置 pg_am.amcanunique 为真。目前,只有 b-tree 支持它。
因为 MVCC ,必须允许重复的条目物理上存在于索引之中:该条目可能指向某个逻辑行的后面的版本。实际想强制的行为是,任何 MVCC 快照都不能包含两条相同的索引键字。这种要求在向一个唯一索引插入新行的时候分解成下面的几种情况:
如果一个有冲突的合法行被当前事务删除,这是可以的。特别是因为一个 UPDATE 总是在插入新版本之前删除旧版本,这样就允许一个行上的 UPDATE 不用改变键字进行操作。
如果一个在等待提交的事务插入了一行有冲突的数据,那么准备插入数据的事务必须等待看看改事务是否提交。如果该事务回滚,那么就没有冲突。如果它没有删除冲突行然后提交,那么就有一个唯一性违例。实际上只是等待另外那个事务结束,然后在程序里重做可视性检查。
类似的,如果一个有冲突的有效行被一个准备提交的事务删除,那么另外一个准备提交的插入事务必须等待该事务提交或者退出,然后重做测试。
此外,根据上面的规则进行唯一性检查之前,访问方法必须重新检查刚被插入的行是否仍然"活跃",如果已经因为事务的提交而"钉死了",那么不应当发出任何错误。这种情况不可能出现在插入同一个事务中创建的行的时候。但是在 CREATE UNIQUE INDEX CONCURRENTLY 的过程中是可能的。
要求索引访问方法自己进行这些测试,这就意味着它必须检查堆,以便查看那些根据索引内容表明有重复键字的任意行的提交状态。这样做毫无疑问地很难看并且也不是模块化的,但是这样可以节约重复的工作:如果进行额外的一次探测,而后面的索引查找冲突行的的动作实际上是和查找插入新行的索引记录重复的动作。并且,没有很显然的方法来避免冲突条件,除非冲突检查是插入新索引条目的整体动作的一部分。
这个方法的主要的局限是没有很方便的方法支持推迟的唯一性检查。