pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
在索引扫描中,索引访问方法负责返回它所知道的、匹配扫描键的所有元组的 TID。访问方法不负责从索引父表中实际取出这些元组,也不负责判断它们是否能通过扫描的可见性测试或其他条件。
扫描键是如下形式的 WHERE 子句的内部表示:index_key operator constant。其中,索引键是该索引的一列,而操作符是与该索引列关联的操作符族成员之一。一次索引扫描可以有零个或多个扫描键,这些扫描键会被隐式地以 AND 连接,也就是说,返回的元组预期应满足所有给定条件。
对于某个特定查询,访问方法可以报告该索引是有损的,或者要求重新检查。这意味着索引扫描会返回所有通过扫描键的条目,并可能额外返回一些未通过扫描键的条目。随后,核心系统的索引扫描机制会再次把索引条件应用到堆元组上,以验证它是否真的应被选中。如果没有指定重检选项,索引扫描就必须精确返回匹配条目的集合。
请注意,确保正确找出通过全部给定扫描键的所有且仅有的条目,这项工作完全由访问方法负责。此外,核心系统只是简单地把所有与索引键和操作符族匹配的 WHERE 子句交给访问方法,而不会做任何语义分析来判断它们是否冗余或互相矛盾。例如,给定 WHERE x > 4 AND x > 14,其中 x 是一个 B-树索引列,那么由 B-树的 amrescan 函数负责识别第一个扫描键是冗余的并可被丢弃。amrescan 期间需要做多少预处理,取决于索引访问方法需要在多大程度上把扫描键归约成一种“正规化”形式。
有些访问方法以明确定义的顺序返回索引条目,另一些则不然。如果 条目按排序后的顺序返回,访问方法应把 pg_am.amcanorder 设为真,表示它 支持有序扫描。所有这类访问方法必须对其等值和排序操作符使用 与 btree 兼容的策略号。
amgettuple 函数有一个 direction 参数,它可以是 ForwardScanDirection(通常情况)或 BackwardScanDirection。如果 amrescan 之后的第一次调用指定了 BackwardScanDirection,那么匹配条件的索引条目 集合就要按从后到前的方向扫描,而不是通常的从前到后,因此 amgettuple 必须返回索引中最后一个匹配元组,而不是 通常情况下的第一个。(这只会发生在声明支持有序扫描的访问方法 上。)第一次调用之后,amgettuple 必须准备好从最近 一次返回条目的位置继续按任一方向推进扫描。(但如果 pg_am.amcanbackward 为假,则后续 所有调用的方向都与第一次相同。)
支持有序扫描的访问方法必须支持在扫描中“标记”一个位置,并在之后返回到该标记位置。同一个位置可能会被恢复多次。不过,每次扫描只需要记住一个位置;新的 ammarkpos 调用会覆盖先前标记的位置。不支持有序扫描的访问方法仍应在 pg_am 中提供标记和恢复函数,但让这些函数在被调用时报错也是可以的。
无论是扫描位置还是标记位置(如果有),在面对索引中的并发插入或删除时都必须保持一致。如果某个新插入的条目没有被一次扫描返回,而假如该条目在扫描开始前就已存在则本应被返回,这也是可以接受的;同样,即使某个条目在第一次扫描通过时未被返回,后续重新扫描或反向扫描时返回它也是可以接受的。类似地,并发删除也可能会或者不会反映在扫描结果中。重要的是,插入或删除本身不能导致扫描漏掉,或者重复返回那些本身并未被插入或删除的条目。
除了使用 amgettuple 之外,索引扫描也可以用 amgetbitmap 一次调用取回所有元组。这可以比 amgettuple 明显更高效,因为它可以避免在访问方法 内部反复加锁/解锁。原则上,amgetbitmap 的效果应与 反复调用 amgettuple 相同,但我们施加了一些限制以 简化问题。首先,amgetbitmap 一次性返回所有元组, 不支持标记或恢复扫描位置。其次,元组是以位图形式返回的,没有 任何特定顺序,这也正是 amgetbitmap 不接受 direction 参数的原因。最后, amgetbitmap 不保证对返回的元组加任何锁,其影响在 第 51.4 节 中有详细说明。
请注意,如果访问方法的内部实现并不适合某一个 API,那么它可以只实现 amgetbitmap 而不实现 amgettuple,反之亦然。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。