IndexScan节点的执行过程由ExecIndexScan函数完成,其执行过程同样由ExecScan统一管理,但对IndexScan节点将使用IndexNext函数来获取元组。ExecIndexScan首先判断是否有RuntimeKeys且需要计算(iss_RuntimeKeyReady为false),如果存在则调用ExecIndexReScan函数计算所有的iss_RuntimeKeys表达式,并将其存储到关联的iss_ScanKeys中。接着调用ExecScan通过IndexNext获取元组,在IndexNext中将调用索引模块提供的index_getnext函数利用索引取得元组。
IndexScan的淸理过程由EndlndexScan函数完成,其中需要回收索引关系描述结构iss_RelationDesc (调用 index_close)和索引扫描描述符 iss_ScanDesc (调用 index_endacan)。
4.IndexOnlyScan节点所谓index only scan ,就是因为建立index时,所包含的字段集合,囊括了我们查询语句中的字段,这样,提取出相应的index ,就不必再次提取数据块了。
举个例子:对于表:
create table test(id int, name text, age int); insert into test select generate_series(1,100000),'test'::text,generate_series(1,100000);我们对id和age建立复合索引:
create index test_id_age on test(id ,age);然后,执行查询:
explain select id, age from test where id < 20 and age >0;查询结果为:
postgres=# explain select id ,age from test where id < 20 and age >0; QUERY PLAN ------------------------------------------------------------------------------- Index Only Scan using test_id_age on test (cost=0.29..41.94 rows=20 width=8) Index Cond: ((id < 20) AND (age > 0)) (2 rows)这个查询里查询的id和age就在索引test_id_age上,在我们取出索引的时候,我们已经获取了(id,age)值的序列,因此就不必再去表中获取记录了,在Index上我们就获得了我们需要的数据,因此称为Index Only Scan。
对这个IndexOnlyScan我们可能有疑问,万一我的索引没有及时更新,岂不是会查询出来旧的过时的数据?
对这点不必担心,我们可以看看IndexOnlyScan的执行函数:
它不是单纯地根据节点的类型给ExecScan的参数AccessMtd和recheckMtd賦予不同的函数指针,而是还要:
* Recalculates the values of any scan keys whose value depends on * information known at runtime, then rescans the indexed relation.也就是说,我们会先重新扫描获取scan key,然后再拿着这个key去scan。调用路径如下:
ExecIndexOnlyScan -->ExecReScan * 这里是rescan,更新scan keys -->ExecReScanIndexOnlyScan -->ExecScan ## 用新的scan keys进行Scan这里,IndexOnlyScan不允许Recheck。
static bool IndexOnlyRecheck(IndexOnlyScanState *node, TupleTableSlot *slot) { elog(ERROR, "EvalPlanQual recheck is not supported in index-only scans"); return false; /* keep compiler quiet */ } 5.BitmapIndexScan 节点BitmapIndexScan节点也是利用属性上的索引进行扫描操作,但是BitmapIndexScan得到的结果不是实际的元组,而是一个位图,该位图标记了满足条件的元组在页面中的偏移量。BitmapIndexScan节点在第一次被执行时就将获取所有满足条件的元组并在位图中标记它们,而其上层节点中都会有特殊的扫描节点(例如下面将介绍的BitmapHeapScan)使用该位图来获取实际的元组。因此,该扫描方式下不产生实际的元组,也就是说,该节点不出现在ExecProcNode函数的调用中,不是一个独立的执行节点,只被特殊的上层节点调用。
BitmapIndexScan与IndexScan节点定义几乎相同,由于一次返回关于所有的元组的位图,所以不需要记录扫描方向的indexorderdir和indexorderby字段。
typedef struct BitmapIndexScan { Scan scan; Oid indexid; /* OID of index to scan */ List *indexqual; /* list of index quals (OpExprs) */ List *indexqualorig; /* the same in original form */ } BitmapIndexScan;