64.3. 索引扫描#
在索引扫描中,索引访问方法负责返回已告知其匹配扫描键的所有元组的 TID。访问方法不参与从索引的父表中实际获取这些元组,也不参与确定它们是否通过扫描的可视性测试或其他条件。
扫描键是WHERE
子句的内部表示形式,格式为*index_key
operator
constant
*,其中索引键是索引的列之一,运算符是与该索引列关联的运算符系列的成员之一。索引扫描具有零个或多个扫描键,这些键隐式地进行 AND 操作——返回的元组应满足所有指示的条件。
访问方法可以报告索引对于特定查询而言是有损的,或者需要重新检查。这意味着索引扫描将返回通过扫描键的所有条目,以及可能不通过的其他条目。然后,核心系统的索引扫描机制将再次将索引条件应用于堆元组,以验证是否真的应该选择它。如果未指定重新检查选项,则索引扫描必须准确返回匹配的条目集。
请注意,完全由访问方法确保其正确查找所有且仅查找通过所有给定扫描键的条目。此外,核心系统将简单地移交所有匹配索引键和运算符系列的WHERE
子句,而不会进行任何语义分析以确定它们是否冗余或矛盾。例如,给定WHERE x > 4 AND x > 14
,其中x
是 b 树索引的列,则由 b 树amrescan
函数来识别第一个扫描键是冗余的,可以丢弃。在amrescan
期间所需的预处理程度将取决于索引访问方法需要将扫描键简化为“规范化”形式的程度。
某些访问方法按明确定义的顺序返回索引条目,而另一些则不。实际上,访问方法支持排序输出有两种不同的方式
始终按其数据的自然顺序返回条目的访问方法(例如 btree)应将
amcanorder
设置为 true。目前,此类访问方法必须为其相等性和排序运算符使用与 btree 兼容的策略编号。支持排序运算符的访问方法应将
amcanorderbyop
设置为 true。这表示索引能够按满足ORDER BY
index_key
operator
constant
的顺序返回条目。如前所述,可以将该形式的扫描修饰符传递给amrescan
。
amgettuple
函数有一个direction
参数,它可以是ForwardScanDirection
(正常情况)或BackwardScanDirection
。如果amrescan
之后的第一个调用指定BackwardScanDirection
,则匹配的索引条目集将从后到前扫描,而不是正常的从前到后方向扫描,因此amgettuple
必须返回索引中的最后一个匹配元组,而不是第一个(这是它通常会做的事情)。(这仅会发生在将amcanorder
设置为 true 的访问方法中。)在第一次调用之后,amgettuple
必须准备好从最近返回的条目中向任一方向推进扫描。(但如果amcanbackward
为 false,则所有后续调用都将与第一次调用具有相同的方向。)
支持有序扫描的访问方法必须支持在扫描中“标记”一个位置,并在之后返回到标记的位置。可以多次恢复同一位置。但是,每个扫描只需要记住一个位置;新的ammarkpos
调用将覆盖先前标记的位置。不支持有序扫描的访问方法不需要在IndexAmRoutine
中提供ammarkpos
和amrestrpos
函数;而是将这些指针设置为 NULL。
扫描位置和标记位置(如果有)必须在索引中并发插入或删除时保持一致。如果扫描未返回扫描开始时存在时扫描本会找到的新插入条目,或者扫描在重新扫描或备份时返回此类条目,即使它在第一次遍历时未返回,也是可以的。同样,并发删除可能会或可能不会反映在扫描结果中。重要的是,插入或删除不会导致扫描遗漏或多次返回未自身插入或删除的条目。
如果索引存储原始索引数据值(而不是它们的某些有损表示),则支持仅索引扫描很有用,其中索引返回实际数据,而不仅仅是堆元组的 TID。仅当可见性映射显示 TID 在一个完全可见的页面上时,这才能避免 I/O;否则,必须访问堆元组以检查 MVCC 可见性。但这不是访问方法的问题。
与使用amgettuple
相比,可以使用amgetbitmap
执行索引扫描,一次性获取所有元组。这比amgettuple
明显更高效,因为它可以避免访问方法中的锁定/解锁周期。原则上,amgetbitmap
应与重复的amgettuple
调用具有相同的效果,但我们施加了一些限制以简化问题。首先,amgetbitmap
一次返回所有元组,不支持标记或恢复扫描位置。其次,元组以没有特定顺序的位图形式返回,这就是amgetbitmap
不采用direction
参数的原因。(对于此类扫描,也永远不会提供排序运算符。)此外,amgetbitmap
不提供仅索引扫描,因为无法返回索引元组的内容。最后,amgetbitmap
不保证对返回的元组进行任何锁定,其含义在第 64.4 节中阐述。
请注意,如果访问方法的内部实现不适合其中一种 API,则允许它仅实现amgetbitmap
而不实现amgettuple
,反之亦然。