PostgreSQL 9.0.4 Documentation | ||||
---|---|---|---|---|
Prev | Fast Backward | Chapter 51. 索引访问方法接口定义 | Fast Forward | Next |
PostgreSQL使用唯一索引来强制SQL唯一约束,唯一 索引实际上是不允许多条记录有相同键值的的索引。一个支持这个特性的访问方法要 设置pg_am.amcanunique为真。目前,只有b-tree 支持它。
因为MVCC,必须允许重复的条目物理上存在于索引之中:该条目可能指向某个逻辑 行的后面的版本。实际想强制的行为是,任何 MVCC 快照都不能包含两条相同的索引 键字。这种要求在向一个唯一索引插入新行的时候分解成下面的几种情况:
如果一个有冲突的合法行被当前事务删除,这是可以的。特别是因为一个 UPDATE总是在插入新版本之前删除旧版本,这样就允许一个行上的UPDATE 不用改变键字进行操作。
如果一个在等待提交的事务插入了一行有冲突的数据,那么准备插入数据的事 务必须等待看看改事务是否提交。如果该事务回滚,那么就没有冲突。如果它 没有删除冲突行然后提交,那么就有一个唯一性违例。实际上只是等待另外那 个事务结束,然后在程序里重做可视性检查。
类似的,如果一个有冲突的有效行被一个准备提交的事务删除,那么另外一个 准备提交的插入事务必须等待该事务提交或者退出,然后重做测试。
此外,根据上面的规则进行唯一性检查之前,访问方法必须重新检查刚被插入的行是 否仍然"活跃",如果已经因为事务的提交而"钉死了",那么不应当发出任何错误。这 种情况不可能出现在插入同一个事务中创建的行的时候。但是在CREATE UNIQUE INDEX CONCURRENTLY的过程中是可能的。
要求索引访问方法自己进行这些测试,这就意味着它必须检查堆,以便查看那些根据 索引内容表明有重复键字的任意行的提交状态。这样做毫无疑问地很难看并且也不是 模块化的,但是这样可以节约重复的工作:如果进行额外的一次探测,而后面的索引 查找冲突行的的动作实际上是和查找插入新行的索引记录重复的动作。并且,没有很 显然的方法来避免冲突条件,除非冲突检查是插入新索引条目的整体动作的一部分。
如果唯一行性约束是可推迟的,这就更加复杂。我们需要能够向新行插入索引记录,
但推迟违反唯一性的错误直到声明的最后或者更迟的时候。为了避免对索引不必要
的重复搜索,索引访问方法应该在最初插入时做一次初步的唯一性检测。如果这个
结果表明记录里确定没有冲突了,就完成了。否则,我们会在该强制限制的时候安
排一个复核出现。(Note that for this purpose, "live" actually
means "any tuple in the index entry's HOT chain is live".)
To implement this, the aminsert
function is passed a
checkUnique parameter having one of the following values:
UNIQUE_CHECK_NO表明不需要做唯一性检测(这不是一个唯一 性索引)。
正如上面所描述的,UNIQUE_CHECK_YES表明有一个不可延迟的唯 一性索引,并且必须立即做唯一性检测。
UNIQUE_CHECK_PARTIAL 表明唯一性约束是可延期的,
PostgreSQL将会用这个模式方法来插入每一行
的索引记录。访问方法必须允许重复的记录进入索引,并且通过从 aminsert
返回FALSE来报告任何可能的重复。对于返回FLASE的每一行,将
调度一个延迟的复核。
访问方法必须能够识别任何可能违反唯一性约束的行,但是对他来说报告假 事实并不是错误。这样就允许检测不必等到其他是物流都结束了才做;冲突 报告在这里不会被当做错误来看待,并且随后将会被重新检测,到那个时候 它们可能不再是冲突了。
UNIQUE_CHECK_EXISTING表明这是一个行的延迟复核,这一行被报
告称之为一个潜在的唯一性违反。尽管这通过调用aminsert
来执
行,在这种情况下这个访问方法绝不能插入一个新的索引记录。因为索引记录已有。
当然,访问方法必须检测以确认是否有另一个索引记录存在。如果是,并且如果
目标行也是仍然存在的,那么报告错误。
我们推荐,在一个UNIQUE_CHECK_EXISTING调用中,访问方法进
一步查证在索引中的目标行实际上并没有一个现存的记录,并且如果不是这样
就报错。这是个好主意,因为被传到aminsert
的索引元组值会
一直被验算。如果索引定义包含可变的函数,我们可能会检测索引的错误区域。
在复查中发现的行标签被用到原始的插入中。