51.3. 索引扫描

在一个索引扫描里,索引访问方法负责把它拿到的那些据说匹配扫描键字scan keys的所有行之 TID 的回流。访问方法不not会卷入从索引的 父表中实际抓取这些行的动作中,也不会判断他们是否通过了扫描的时间条件测试或者 是其它条件。

一个扫描键字是形如 index_key operator constantWHERE 子句的内 部表现形式,这里的索引键字是索引中的一个字段,而操作符是和该索引字段相关联的 操作符类的一个成员。一个索引扫描拥有零个或者多个扫描键字,他们是隐含着 AND 的关系 —返回的行被认为是满所所有列出的条件的行。

操作符类可能会指出改索引对于某些特定的操作符是有损耗的;这就暗示着该索引扫 描会返回所有通过扫描键字的条目,加上一些可能没通过扫描键字的条目。核心系统 的索引扫描机制然后就会再次在堆行上使用该操作符,以校验这些条目是否真正应该 选取。对于无损耗的操作符,索引扫描必须返回全部匹配的条目,不需要重复校验。

请注意,确保找到所有条目以及确保所有条目都通过给出的扫描键字的条件完全是 访问方法的责任。还有,核心系统将只是简单的吧所有匹配扫描键字和操作符类的 WHERE子句传递过来,而不会做任何语义分析,以判断他们是否冗余 或者是否相互矛盾。举例来说,给出 WHERE WHERE x > 4 AND x > 14 ,这里的 x是一个 b-tree 索引字段,那么把第一个扫描键字 识别成冗余的和可抛弃的工作是 b-tree amrescan 函数的事。 amrescan 过程中所需要的预处理的范围将由索引访问方法把扫描键 字缩减为一个"正常"形式的具体需要而定。

一些访问方法按照一个明确定义的顺序来返回索引条目,其他的则不会。如果条目按分类顺序返回, 访问方法应该设置pg_amamcanorder 对指示为真因此它支持顺 序扫描。所有这些访问方法必须为其等式和排序操作使用可兼容b树的模式编码。

amgettuple函数有一个direction参数,它可以是 ForwardScanDirection(正常情况)或者BackwardScanDirection 。如果amrescan之后的第一次调用声明 BackwardScanDirection,那么匹配条件的索引记录集是从后向前扫描的,而 不是通常的从前向后扫描,因此 amgettuple 必须返回索引中最后 的匹配行,而不是通常情况下的第一条。这些事情只会是那些设置了 pg_am.amcanbackward非零的,号称自己支持排序扫描的访问 方法上会发生。在第一次调用之后,amrescan必须准备从最近返回 的条目的位置开始,在两个方向上进行扫描步进。(但假如pg_amamcanbackward是假,所有后来的调用将会有与第一个同样的方位。)

支持有序扫描的访问方法必须支持在扫描里“标记”一个位置并且随后返回到这个标记 过的位置。同样的位置可能会被重复多次还原。但是,每次扫描中只有一个位置需要 被记住;一个新的ammarkpos 调用重写之前标记的位置。一个不支持有 序扫描的访问方法应该仍然在pg_am中提供标记和还原功能,但如果 被访问它足以使他们抛出错误。

扫描位置和标记位置(如果存在)都必须在面对索引中存在并发插入和删除的时候保持 一致性。如果一条并发新插入的记录并未被一次扫描返回(而如果扫描开始的时候该 记录存在,则会被返回),或者说扫描通过重新扫描或者回头扫描返回这样的记录—即 使它第一次跑的时候没有返回这样的行,对于系统来说,这些情况都是可以接受的。 类似的还有,一个并发的删除可以反映,也可以不反应一个扫描的结果。重要的是, 插入或者删除不会导致扫描会略过或者重复返回本身不是被插入或者删除的条目。

除了使用 amgettuple,索引扫描可以通过amgetmulti 在一次调用中提取所有元组来完成。这样做可能会比amgettuple 有显 著的效率提升,因为它可以避免在访问方法内的加锁/解锁的循环。在原理上, amgetmulti 应该和重复调用 amgettuple的效果相同, 不过我们强制了一些限制来简化事情。首先,amgetmulti同时返回 所有元组并且标记或扫描不支持的扫描条件。第二,这些元组被返回到一个没有任何 特定排序的位图中,这就是amgetbitmap不获取direction 参数的原因。amgetmulti并不保证在返回的行上的任何锁定,这就暗示 着Section 51.4里面的事情。

注意下,对于一个访问方法来说,如果他的内部实现不适用于一个API或者另一个, 仅仅执行amgetbitmap而不执行amgettuple是被允许的 ,或者反过来也是一样的。