Index Scanning
在索引扫描中,索引访问方法负责对已被告知与扫描键匹配的所有元组的TIDs进行反求。访问方法不涉及从索引父表中实际获取这些元组,也不涉及确定它们是否通过扫描的时间限定性测试或其他条件。
扫描键是窗体index_key运算符常数的WHERE子句的内部表示,其中索引键是索引的列之一,运算符是与该索引列关联的运算符族的成员之一。索引扫描具有零或多个扫描密钥,这些密钥是隐式的-返回的元组期望满足所有指示的条件。
访问方法可以报告索引对于特定查询是有损的,或者需要重新检查。这意味着索引扫描将返回通过扫描键的所有条目,以及可能不附加的条目。然后,核心系统的索引扫描机制将索引条件再次应用到堆元组,以验证它是否真的应该被选择。如果未指定重新检查选项,则索引扫描必须完全返回匹配项集。
请注意,完全取决于访问方法,以确保它正确地找到所有和仅通过所有给定扫描键的条目。此外,核心系统将简单地切换所有与索引关键字和操作符族匹配的WHERE子句,而无需任何语义分析来确定它们是冗余的还是矛盾的。例如,在x>4和x>14的情况下,X是B树索引列,它留给B树amrescan函数来实现第一扫描密钥是冗余的并且可以被丢弃。amrescan中需要的预处理的程度将取决于索引访问方法需要将扫描密钥减少到“标准化”形式的程度。
一些访问方法以良好定义的顺序返回索引条目,而其他则不返回。实际上,访问方法可以支持分拣输出的两种不同方式
- 总是在其数据的自然排序(如b-tree)中返回条目的访问方法应该将amcanorder设置为true。目前,这种访问方法必须使用b-tree兼容的策略号来实现它们的相等和排序算子。
- 支持排序操作符的访问方法应该将amcanorderbyop设置为true。这表明该索引能够以按索引索引键运算符常数的顺序返回条目。这种形式的扫描修改器可以像前面描述的那样传递给amrescan。
amgettuple函数有一个方向参数,它可以是转发扫描方向(正常情况)或向后扫描方向。如果amrescan之后的第一次调用指定向后扫描方向,那么匹配索引条目的集合将被扫描回前部而不是在正常的前向向后的方向上,因此amgettuple必须返回索引中的最后匹配元组,而不是返回第一个匹配元组。(这只会对设置amcanorder为true的访问方法发生)。在第一次调用之后,amgettuple必须准备好从最近返回的条目向任一方向前进扫描。(但如果amcanbackward为false,所有后续调用将具有与第一个调用相同的方向。)
支持顺序扫描的访问方法必须支持“标记”扫描中的位置,然后再返回标记位置。相同的位置可能会多次恢复。然而,每次扫描只需要记住一个位置;新的ammarkpos调用重写先前标记的位置。不支持顺序扫描的访问方法不需要在IndexAmRoutine中提供ammarkpos and amrestrpos函数;而是将这些指针设置为NULL。
在索引中同时插入或删除时,扫描位置和标记位置(如果有的话)都必须保持一致。如果新插入的条目没有在扫描开始时已经存在的条目返回,或者扫描在扫描或备份时返回这样的条目,即使第一次未返回,也可以。类似地,并发删除可能会或可能不会反映在扫描结果中。重要的是插入或删除不导致扫描错过或乘以未插入或删除的返回条目。
如果索引存储原始索引数据值(而不是它们的一些有损表示),则只支持index-only scans,其中索引返回实际数据而不只是堆元组的TID。如果可见性图显示TID在所有可见页上,这将仅避免I/O,则无论如何必须访问堆元组以检查MVCC可见性。但这不是访问方法的问题。
代替使用amgettuple,索引扫描可以用amgetbitmap进行,以在一个调用中获取所有元组。这可以明显地比amgettuple更有效,因为它允许避免访问方法中的锁定/解锁周期。原则上,amgetbitmap应该具有与重复的amgettuple调用相同的效果,但是我们强加了几个限制来简化事务。首先,amgetbitmap一次返回所有元组,不支持标记或恢复扫描位置。第二,元组返回一个没有特定排序的位图,这就是为什么amgetbitmap不采用方向参数。(对于这种扫描,也不提供排序运算符)。此外,由于无法返回索引元组的内容,所以不提供使用amgetbitmap的仅索引扫描的规定。最后,amgetbitmap不能保证返回的元组的任何锁定。
注意,如果其内部实现不适合一个API或另一个API,则允许访问方法只实现amgetbitmap,而不是amgettuple,反之亦然。