PostgreSQL 索引扫描
在一个索引扫描中,索引访问方法负责提供它拿到的匹配扫描键的所有元组的TID。访问方法不会涉及从索引的父表中实际取得那些元组,也不会涉及判断它们是否通过了扫描的可见性测试或者是其它条件。
一个扫描键是一个WHERE
子句的内部表示,WHERE
子句的形式是index_key
operator
constant
,其中索引键字索引中的一个列,而操作符是和该索引列相关联的操作符族的一个成员。一个索引扫描拥有零个或者多个扫描键,它们是隐式
AND 关系 — 返回的元组被认为满足所有列出的条件。
对于一个特定查询,访问方法可能报告索引是有损的或者要求重新检查。这就暗示着该索引扫 描会返回所有通过扫描键的项,外加上一些可能没通过扫描键的项。核心系统的索引扫描机制然后就会再次在堆元组上应用索引条件来验证它是否真地应该被选择。如果没有指定重新检查选项,索引扫描必须返回准确的匹配项集合。
请注意,确保找到所有(只有)通过所有给定扫描键的条目的工作完全由访问方法负责。还有,核心系统将只是简单地 放过所有匹配扫描键和操作符族的WHERE
子句,而不会做任何语义分析来判断它们是否冗余或者矛盾。例如,给定WHERE x > 4 AND x > 14
(其中x
是一个 B-树 索引列,它被留给 B-树
amrescan
函数来发现第一个扫描键是冗余并且可以被丢弃。amrescan
期间需要的预处理的范围将取决于索引访问方法需要什么来把扫描键缩减为一种“正规化的”形式。
一些访问方法按照一个良定义的顺序来返回索引项,其他的则不会。实际上一个访问方法可以有两种不同的方式支持排序输出:
-
总是按数据的自然序返回项的访问方法应该设置
amcanorder
为真。当前,这样的访问方法必须对它们的等值和排序操作符使用b-tree兼容的策略号。 -
支持排序操作符的访问方法应该设置
amcanorderbyop
为真。这表示索引有能力按照满足ORDER BY
index_key
operator
constant
的一种顺序返回项。如前所述,这种形式的扫描修饰符可以被传递给amrescan
。
amgettuple
函数有一个direction
参数,它可以是 ForwardScanDirection
(正常情况)或者BackwardScanDirection
。如果amrescan
之后的第一次调用指定了
BackwardScanDirection
,那么匹配条件的索引项集合是从后向前扫描的,而 不是通常的从前向后扫描,因此amgettuple
必须返回索引中最后一个匹配元组,而不是通常情况下的第一个(这只对设置了amcanorder
为真访问方法发生)。在第一次调用后,amgettuple
必须被准备好从最近被返回项的位置按照任何一种方向推进扫描(但是如果
amcanbackward
为假,所有后续调用将使用第一次相同的方向)。
支持排序扫描的访问方法必须支持在扫描里“标记”一个位置并且随后返回到这个标记过的位置。同一个位置可能会被重复多次还原。但是,每个扫描中只有一个位置需要被记住;一个新的ammarkpos
调用将重写之前标记的位置。一个不支持排序扫描的访问方法无需在IndexAmRoutine
中提供
ammarkpos
和amrestrpos
函数,把这些指针设置为 NULL 即可。
扫描位置和标记位置(如果存在))都必须在面对索引中的并发插入和删除时保持一致性。如果一个新插入的项并未被一个扫描返回(如果该扫描开始的时候该项已经存在,该扫描将已经找到该项),或者说扫描通过重新扫描或者反向扫描返回这样一个项(即使它第一次没有返回这样一个项),这些情况都是可以接受的。类似的还有,一个并发的删除可能或不可能被反映在一个扫描的结果中。重要的是,插入或者删除不会导致扫描错过或者多次返回本身不是被插入或者删除的项。
如果索引存储原始被索引的数据值(并且不是它们的某种有损表示),它可用来支持只用索引的扫描,着这种扫描中索引返回的就是实际的数据而不只是堆元组的 TID。这只有在可见性映射显示该 TID 位于一个全部可见的页面时才能避免 I/O;否则必须访问堆元组来检查 MVCC 可见性。但是这就不用访问方法操心了。
除了使用amgettuple
,一个索引扫描可以通过amgetbitmap
在一次调用中取得所有元组来完成。这样做可能会比amgettuple
有显著的效率提升,因为它可以避免在访问方法内的加锁/解锁循环。原则上amgetbitmap
应该和重复调用amgettuple
的效果相同,
不过我们强加了一些限制来简化这件事。首先,amgetbitmap
一次返回 所有元组并且标记并且不支持标记或恢复扫描位置。第二,在一个位图中返回的元组没有任何指定的顺序,这也是为什么amgetbitmap
没有一个direction
参数的原因(排序操作符也将永远不会提供给这种扫描)。还有,对于使用amgetbitmap
的只用索引扫描没有规定,因为没有办法返回索引元组的内容。最后,如
第 61.4 节中所说的,amgetbitmap
不保证被返回元组上的任何锁。
注意如果访问方法的内部实现不适合一个 API 或其他 API,允许一个访问方法只实现amgetbitmap
而不实现amgettuple
,或者反过来。