Redrock Postgres 搜索 英文
版本: 9.3 / 9.4 / 9.5 / 9.6 / 10 / 11 / 12 / 13 / 14 / 15 / 16

64.3. 索引扫描 #

在索引扫描中,索引访问方法负责返回已告知其匹配扫描键的所有元组的 TID。访问方法参与从索引的父表中实际获取这些元组,也不参与确定它们是否通过扫描的可视性测试或其他条件。

扫描键是WHERE子句的内部表示形式,格式为index_key operator constant,其中索引键是索引的列之一,运算符是与该索引列关联的运算符系列的成员之一。索引扫描具有零个或多个扫描键,这些键隐式地进行 AND 操作——返回的元组应满足所有指示的条件。

访问方法可以报告索引对于特定查询而言是有损的,或者需要重新检查。这意味着索引扫描将返回通过扫描键的所有条目,以及可能不通过的其他条目。然后,核心系统的索引扫描机制将再次将索引条件应用于堆元组,以验证是否真的应该选择它。如果未指定重新检查选项,则索引扫描必须准确返回匹配的条目集。

请注意,完全由访问方法确保其正确查找所有且仅查找通过所有给定扫描键的条目。此外,核心系统将简单地移交所有匹配索引键和运算符系列的WHERE子句,而不会进行任何语义分析以确定它们是否冗余或矛盾。例如,给定WHERE x > 4 AND x > 14,其中x是 b 树索引的列,则由 b 树amrescan函数来识别第一个扫描键是冗余的,可以丢弃。在amrescan期间所需的预处理程度将取决于索引访问方法需要将扫描键简化为规范化形式的程度。

某些访问方法按明确定义的顺序返回索引条目,而另一些则不。实际上,访问方法支持排序输出有两种不同的方式

amgettuple 函数有一个 direction 参数,它可以是 ForwardScanDirection(正常情况)或 BackwardScanDirection。如果 amrescan 之后的第一个调用指定 BackwardScanDirection,则匹配的索引条目集将从后到前扫描,而不是正常的从前到后方向扫描,因此 amgettuple 必须返回索引中的最后一个匹配元组,而不是第一个(这是它通常会做的事情)。(这仅会发生在将 amcanorder 设置为 true 的访问方法中。)在第一次调用之后,amgettuple 必须准备好从最近返回的条目中向任一方向推进扫描。(但如果 amcanbackward 为 false,则所有后续调用都将与第一次调用具有相同的方向。)

支持有序扫描的访问方法必须支持在扫描中“标记”一个位置,并在之后返回到标记的位置。可以多次恢复同一位置。但是,每个扫描只需要记住一个位置;新的 ammarkpos 调用将覆盖先前标记的位置。不支持有序扫描的访问方法不需要在 IndexAmRoutine 中提供 ammarkposamrestrpos 函数;而是将这些指针设置为 NULL。

扫描位置和标记位置(如果有)必须在索引中并发插入或删除时保持一致。如果扫描未返回扫描开始时存在时扫描本会找到的新插入条目,或者扫描在重新扫描或备份时返回此类条目,即使它在第一次遍历时未返回,也是可以的。同样,并发删除可能会或可能不会反映在扫描结果中。重要的是,插入或删除不会导致扫描遗漏或多次返回未自身插入或删除的条目。

如果索引存储原始索引数据值(而不是它们的某些有损表示),则支持仅索引扫描很有用,其中索引返回实际数据,而不仅仅是堆元组的 TID。仅当可见性映射显示 TID 在一个完全可见的页面上时,这才能避免 I/O;否则,必须访问堆元组以检查 MVCC 可见性。但这不是访问方法的问题。

与使用 amgettuple 相比,可以使用 amgetbitmap 执行索引扫描,一次性获取所有元组。这比 amgettuple 明显更高效,因为它可以避免访问方法中的锁定/解锁周期。原则上,amgetbitmap 应与重复的 amgettuple 调用具有相同的效果,但我们施加了一些限制以简化问题。首先,amgetbitmap 一次返回所有元组,不支持标记或恢复扫描位置。其次,元组以没有特定顺序的位图形式返回,这就是 amgetbitmap 不采用 direction 参数的原因。(对于此类扫描,也永远不会提供排序运算符。)此外,amgetbitmap 不提供仅索引扫描,因为无法返回索引元组的内容。最后,amgetbitmap 不保证对返回的元组进行任何锁定,其含义在 第 64.4 节 中阐述。

请注意,如果访问方法的内部实现不适合其中一种 API,则允许它仅实现 amgetbitmap 而不实现 amgettuple,反之亦然。