受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: devel

60.3. 索引扫描 #

在索引扫描中,索引访问方法负责返回它所知道的、匹配扫描键的所有元组的 TID。访问方法负责从索引父表中实际取出这些元组,也不负责判断它们是否能通过扫描的可见性测试或其他条件。

扫描键是如下形式的 WHERE 子句的内部表示:index_key operator constant。其中,索引键是该索引的一列,而操作符是与该索引列关联的操作符族成员之一。一次索引扫描可以有零个或多个扫描键,这些扫描键会被隐式地以 AND 连接,也就是说,返回的元组预期应满足所有给定条件。

对于某个特定查询,访问方法可以报告该索引是有损的,或者要求重新检查。这意味着索引扫描会返回所有通过扫描键的条目,并可能额外返回一些未通过扫描键的条目。随后,核心系统的索引扫描机制会再次把索引条件应用到堆元组上,以验证它是否真的应被选中。如果没有指定重检选项,索引扫描就必须精确返回匹配条目的集合。

请注意,确保正确找出通过全部给定扫描键的所有且仅有的条目,这项工作完全由访问方法负责。此外,核心系统只是简单地把所有与索引键和操作符族匹配的 WHERE 子句交给访问方法,而不会做任何语义分析来判断它们是否冗余或互相矛盾。例如,给定 WHERE x > 4 AND x > 14,其中 x 是一个 B-树索引列,那么由 B-树的 amrescan 函数负责识别第一个扫描键是冗余的并可被丢弃。amrescan 期间需要做多少预处理,取决于索引访问方法需要在多大程度上把扫描键归约成一种正规化形式。

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

  • 总是按数据自然顺序返回条目的访问方法(例如 btree)应把 amcanorder 设为真。目前,这类访问方法必须对其等值和排序操作符使用与 btree 兼容的策略号。

  • 支持排序操作符的访问方法应把 amcanorderbyop 设为真。这表示索引能够按满足 ORDER BY index_key operator constant 的顺序返回条目。前面已经提到,这种形式的扫描修饰符可以传给 amrescan

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

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

无论是扫描位置还是标记位置(如果有),在面对索引中的并发插入或删除时都必须保持一致。如果某个新插入的条目没有被一次扫描返回,而假如该条目在扫描开始前就已存在则本应被返回,这也是可以接受的;同样,即使某个条目在第一次扫描通过时未被返回,后续重新扫描或反向扫描时返回它也是可以接受的。类似地,并发删除也可能会或者不会反映在扫描结果中。重要的是,插入或删除本身不能导致扫描漏掉,或者重复返回那些本身并未被插入或删除的条目。

如果索引存储的是原始被索引数据值(而不是它们的某种有损表示),那么它就适合支持 仅索引扫描,在这种扫描中索引返回的是实际数据,而不只是堆元组的 TID。只有当可见性映射显示该 TID 位于一个全部可见的页面上时,这才能避免 I/O;否则仍然必须访问堆元组来检查 MVCC 可见性。不过,这并不是访问方法需要关心的事。

除了使用 amgettuple 外,索引扫描也可以使用 amgetbitmap,在一次调用中取出所有元组。与 amgettuple 相比,这通常明显更高效,因为它可以避免访问方法内部的反复加锁和解锁。原则上,amgetbitmap 应具有与重复调用 amgettuple 相同的效果,但为了简化实现,我们施加了若干限制。首先,amgetbitmap 一次性返回全部元组,因此不支持标记或恢复扫描位置。其次,元组是通过一个位图返回的,没有特定顺序,这也是为什么 amgetbitmap 不带 direction 参数的原因。(这种扫描也永远不会提供排序操作符。)此外,使用 amgetbitmap 时也没有仅索引扫描的支持,因为没有办法返回索引元组的内容。最后,amgetbitmap 不保证对返回的元组做任何锁定,其影响见 Section 60.4

请注意,如果访问方法的内部实现并不适合某一个 API,那么它可以只实现 amgetbitmap 而不实现 amgettuple,反之亦然。

提交更正

如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。