ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 54.3\. 索引扫描 在一个索引扫描里,索引访问方法负责把它拿到的那些据说匹配_扫描键字_的所有行之 TID 的回流。 访问方法_不会_卷入从索引的父表中实际抓取这些行的动作中,也不会判断他们是否通过了扫描的时间条件测试或者是其它条件。 一个扫描键字是形如`_index_key_` `_operator_``_constant_` 的`WHERE`子句的内部表现形式, 这里的索引键字是索引中的一个字段,而操作符是和该索引字段相关联的操作符族的一个成员。 一个索引扫描拥有零个或者多个扫描键字,他们是隐含着 AND 的关系 —返回的行被认为是满所所有列出的条件的行。 访问方法可以声称自己是_有损的_或对特定的查询需要再检查。 这意味着,索引扫描会返回所有通过扫描键字的条目并可能加上一些不能通过的条目。 核心系统的索引扫描装置之后会对堆元组应用索引条件以确认是否真的这个元组应该被选中。 如果再检查选项没有被指定,索引扫描必须精确返回匹配的条目。 请注意,确保找到所有条目以及确保所有条目都通过给出的扫描键字的条件完全是访问方法的责任。 还有,核心系统将只是简单的把所有匹配扫描键字和操作符族的`WHERE`子句传递过来,而不会做任何语义分析,以判断他们是否冗余或者是否相互矛盾。 举例来说,给出`WHERE x > 4 AND x > 14` where `x` ,这里的 `x`是一个 b-tree 索引字段, 那么把第一个扫描键字识别成冗余的和可抛弃的工作是 b-tree `amrescan`函数的事。 `amrescan`过程中所需要的预处理的范围将由索引访问方法把扫描键字缩减为一个"规范化"形式的具体需要而定。 一些访问方法以明确的顺序返回索引项,另外一些则不是。 实际上访问方法可以支持两种不同的排序输出方法: * 以自然的数据顺序返回条目的访问方法(比如btree)应该设置`pg_am`.`amcanorder`为真。 当前,这样的访问方法必须为它们的相等和排序操作符使用btree兼容的策略数。 * 支持排序操作的访问方法必须设置`pg_am`.`amcanorderbyop`为真。 这表明这个索引能够以满足`ORDER BY` `_index_key_``_operator_` `_constant_` 的顺序返回条目。 这种形式的扫描修饰子能够像前面描述的那样被传入`amrescan`。 `amgettuple`函数有一个`direction`参数,它可以是`ForwardScanDirection` (正常情况)或者 `BackwardScanDirection` 。 如果`amrescan`之后的第一次调用声明`BackwardScanDirection`,那么匹配条件的索引记录集是从后向前扫描的,而不是通常的从前向后扫描, 因此`amgettuple`必须返回索引中最后的匹配行,而不是通常情况下的第一条。 (这些事情只会在那些设置了`amcanorder`为真的访问方法上会发生。) 在第一次调用之后,`amgettuple`必须准备从最近返回的条目的位置开始,在两个方向上进行扫描步进。 (但是,如果`pg_am`.`amcanbackward`为假,所有随后的调用将会和第一次调用使用相同的顺序。) 支持排序扫描的访问方法必须支持在扫描上"标记"位置,并在之后可以返回这个被标记的位置。 相同的位置可能被恢复多次。 然而,每次扫描只有一个位置需要被记住;一次新的`ammarkpos`调用会覆盖先前被标记的位置。 不支持排序扫描的访问方法仍然可以在`pg_am`中提供标记和恢复函数,但是如果被调用可以让这些函数抛出错误。 扫描位置和标记位置(如果存在)都必须在面对索引中存在并发插入和删除的时候保持一致性。 如果一条并发新插入的记录并未被一次扫描返回(而如果扫描开始的时候该记录就存在的话,则会被返回), 或者说扫描通过重新扫描或者回头扫描返回这样的记录(即使它第一次跑的时候没有返回这样的行),对于系统来说,这种情况是可以接受的。 类似的还有,一个并发的删除可以反映,也可以不反应一个扫描的结果。 重要的是,插入或者删除不会导致扫描会略过或者重复返回本身不是被插入或者删除的条目。 如果索引存储原始的被索引数据值(而不是某种有损的表现形式),对支持index-only扫描是有用的, 这时,索引返回实际的数据而不仅是堆元组的TID。 只有在可见性映射显示这个TID在一个全可见页上是这才有效;否则还是必须检查堆元组的MVCC可见性。 但是访问方法不需要关心这件事。 作为`amgettuple`的替代,索引扫描可以可以通过一次`amgetbitmap`调用抓取所有元组来完成。 这明显可能比`amgettuple`更高效,因为可以避免在访问方法内部的加锁解锁。 一般而言,`amgetbitmap`和重复调用 `amgettuple`有相同的效果, 但是为了简化我们加入了一些限制。 首先,`amgetbitmap`一次返回所有元组,并且不支持标记和恢复位置。 第二,在bitmap中返回的元组没有任何指定的顺序,这也是`amgetbitmap`没有`direction`参数的原因。 (排序操作也就永远不会被应用到这样的扫描上) 既然没有办法返回被索引元组的内容,也就不会有使用`amgetbitmap`的index-only扫描。 最后,`amgetbitmap`不能保证对被返回元组上实施任何在[Section 54.4](#calibre_link-1144)中描述的锁。 注意,如果一个访问方法的内部实现不适合实现其中一个API的话, 允许一个访问方法只实现`amgetbitmap`和`amgettuple`中的一个。