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

62.3. 索引扫描 #

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

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

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

请注意,确保正确地查找并且仅查找通过所有给定扫描键的所有项,这完全取决于访问方法。此外,核心系统将简单地移交所有与索引键和操作符族匹配的WHERE子句,而无需任何语义分析来确定这些子句是否冗余或矛盾。例如,给定WHERE x > 4 AND x > 14,其中x是 b 树索引的列,b 树amrescan函数将负责认识到第一个扫描键是冗余的,并且可以丢弃。在amrescan期间需要的预处理程度将取决于索引访问方法需要将扫描键简化为标准化表单的程度。

某些访问方法以明确定义的顺序返回索引项,而另一些访问方法则不。实际上,访问方法可以使用两种不同的方式来支持排序输出。

amgettuple 函数具有 direction 参数,该参数可以是 ForwardScanDirection(普通情况)或 BackwardScanDirection。如果在 amrescan 之后的第一个调用指定了 BackwardScanDirection,那么匹配的索引项集合将按从后到前的顺序扫描,而不是从前的到后的普通顺序,所以 amgettuple 必须返回索引中的最后一个匹配元组,而不是普通情况下返回的第一个元组。(这仅会发生对于将 amcanorder 设置为 true 的访问方法。)在第一次调用之后, amgettuple 必须准备在 scan 中从最近返回的项按任意方向推进。(但是,如果 amcanbackward 为 false,那么所有后续调用将具有与第一次调用相同的顺序。)

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

在索引中并发插入或删除时,扫描位置和标记位置(如果存在)必须一致地保持。如果新插入的项未被扫描返回(如果该项在扫描开始时已经存在,扫描将会找到该项),或扫描在重新扫描或回退时返回该项(即使该项在第一次扫描时未返回)是可以的。类似地,并发删除可能或可能不会反映在扫描结果中。重要的是插入或删除不会导致扫描遗漏或重复返回正在插入或删除的项。

如果索引存储原始的索引数据值(而非其某些无损表示),则支持仅索引扫描是有用的,在这种情况下,索引返回的不仅是堆元组的 TID,还有实际数据。仅当可见性映射显示 TID 位于完全可见页面上时,这才会避免 I/O;否则仍然必须访问堆元组才能检查 MVCC 可见性。但这与访问方法无关。

代替使用 amgettuple,可以使用 amgetbitmap 执行索引扫描以一次调用取回所有元组。这明显比 amgettuple 更有效,因为它允许避开访问方法中的锁/解锁循环。从原理上讲,amgetbitmap 应与重复调用 amgettuple 有相同的效果,但是我们施加了若干限制以简化问题。首先,amgetbitmap 一次返回所有元组,并且不支持标记或还原扫描位置。其次,元组在一个没有任何特定顺序的位图中返回,这就是 amgetbitmap 不采用 direction 参数的原因。(排序运算符也永远不会针对此类扫描提供。)此外,由于无法返回索引元组的内容,因此 amgetbitmap 不提供专用于索引的扫描。最后,amgetbitmap 不保证对返回的元组进行任何锁定,这意味着对 62.4 节中拼写出来的内容有影响。

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