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

58.3. 索引扫描

在一个索引扫描中,索引访问方法负责回流它已被告知的那些据说匹配扫描键 的所有元组的 TID。访问方法会涉及从索引的父表中实际取得那些元组, 也不会涉及判断它们是否通过了扫描的时间条件测试或者是其它条件。

一个扫描键是一个WHERE子句的内部表示,WHERE子句的形式是 index_key operator constant, 其中索引键是索引中的一个列,而操作符是和该索引列相关联的操作符族的一个成员。 一个索引扫描拥有零个或者多个扫描键,它们是隐式 AND 关系 — 返回的元组被认为满足所有列出的条件。

对于一个特定查询,访问方法可能报告索引是有损的或者要求重新检查。 这就暗示着该索引扫描会返回所有通过扫描键的项,外加上一些可能没通过扫描键的项。 核心系统的索引扫描机制然后就会再次在堆元组上应用索引条件来验证它是否真地应该被选择。 如果没有指定重新检查选项,索引扫描必须返回准确的匹配项集合。

请注意,确保找到所有通过了所有给定扫描键的条目的工作完全取决于访问方法。 还有,核心系统将只是简单地传递所有匹配扫描键和操作符族的WHERE子句, 而不会做任何语义分析来判断它们是否冗余或者矛盾。例如, 给定WHERE x > 4 AND x > 14(其中x是一个 b-tree 索引列, 它被留给 b-tree amrescan函数来发现第一个扫描键是冗余并且可以被丢弃。 amrescan期间需要的预处理程度将取决于索引访问方法把扫描键缩减为一种 "规范化"形式的程度。

一些访问方法以明确的顺序返回索引项,其他的则不会。 实际上一个访问方法可以支持两种不同的排序输出方法:

amgettuple函数有一个direction参数,它可以是 ForwardScanDirection(正常情况)或者BackwardScanDirection。 如果amrescan之后的第一次调用指定了BackwardScanDirection, 那么匹配条件的索引项集合是从后向前扫描的,而不是通常的从前向后扫描, 因此amgettuple必须返回索引中最后一个匹配元组,而不是通常情况下的第一个 (这只对设置了amcanorder为真的访问方法发生)。在第一次调用后, amgettuple必须被准备好从最近被返回项的位置按照任何一种方向推进扫描 (但是如果pg_am.amcanbackward为假, 所有后续调用将使用第一次调用相同的方向)。

支持排序扫描的访问方法必须支持在扫描上"标记"一个位置并且随后返回到这个被标记的位置。 同一个位置可能会被恢复多次。但是,每个扫描中只有一个位置需要被记住; 一个新的ammarkpos调用将覆盖之前被标记的位置。 一个不支持排序扫描的访问方法仍然应该在pg_am中提供标记和还原函数, 在它们被调用时抛出错误就足够了。

扫描位置和标记位置(如果存在)都必须在面对索引中的并发插入和删除时保持一致性。 如果一个新插入的项并未被一个扫描返回(如果该扫描开始的时候该项已经存在,该扫描将已经找到该项), 或者说扫描通过重新扫描或者反向扫描返回这样一个项(即使它第一次没有返回这样一个项), 这些情况都是可以接受的。类似的还有,一个并发的删除可能或可能不被反映在一个扫描的结果中。 重要的是,插入或者删除不会导致扫描错过或者多次返回本身不是被插入或者删除的项。

如果索引存储原始被索引的数据值(并且不是它们的某种有损表示),它可用来支持只用索引的扫描, 这种扫描中索引返回的就是实际的数据而不只是堆元组的 TID。 这只有在可见性映射显示该 TID 位于一个全部可见的页面时才工作; 否则必须访问堆元组来检查 MVCC 可见性。但是这就不用访问方法操心了。

除了使用amgettuple,一个索引扫描可以通过amgetbitmap 在一次调用中取得所有元组来完成。这样做可能会比amgettuple更高效, 因为它可以避免在访问方法内的加锁/解锁循环。原则上amgetbitmap应该和重复调用 amgettuple的效果相同,不过我们加入了一些限制来简化这件事。首先, amgetbitmap一次返回所有元组并且不支持标记或恢复扫描位置。第二, 在一个位图中返回的元组没有任何指定的顺序,这也是为什么amgetbitmap 没有direction参数的原因(排序操作符也将永远不会提供给这种扫描)。 还有,没有为仅索引的扫描提供amgetbitmap, 因为没有办法返回索引元组的内容。最后,如第 58.4 节中所说的, amgetbitmap不保证被返回元组上的任何锁。

注意如果访问方法的内部实现不适合一个 API 或其他 API, 允许一个访问方法只实现amgetbitmap而不实现amgettuple,或者反过来。

<
/BODY >