您的位置:首页 > 数据库

PostgreSQL查询代价估算(三)

2017-03-13 18:55 197 查看

1.1.1.1    索引扫描的代价:cost_index

函数功能:

某个索引获取元组的花费。不同类型的索引,有着不同的基本属性值的(如选择率、索引相关度等)花费估算函数(在pg_am.h的amcostestimate列上定义)。索引扫描代价,在PostgreSQL中分为普通索引的扫描代价、只读索引扫描的代价。只读索引扫描因为不需要访问数据文件,所以可以减少花费。不管是哪种方式,基本原理都遵从代价估算模型“总代价 = I/O代价 + CPU代价”;局部差别,只是根据实际的扫描过程所做的工作,对每个主要步骤做花费计算。索引扫描有别于顺序扫描,是增加了对索引文件的访问,这项花费需要计算。

 

代码分析:

void

cost_index(IndexPath *path, PlannerInfo *root, double loop_count)

{......

       if
(!enable_indexscan)

              startup_cost
+= disable_cost;

 

    //每一类索引都有自己的花费基本值估算函数,如btree索引的花费估算函数是btcostestimate函数。对于不同的表达式,

       OidFunctionCall7(index->amcostestimate,

                                    PointerGetDatum(root),

                                    PointerGetDatum(path),

                                    Float8GetDatum(loop_count),

                                    PointerGetDatum(&indexStartupCost), //索引启动花费

                                    PointerGetDatum(&indexTotalCost), //索引总花费

                                    PointerGetDatum(&indexSelectivity),//索引选择率

                                    PointerGetDatum(&indexCorrelation)); //索引相关度

......

       startup_cost
+= indexStartupCost;

       run_cost
+= indexTotalCost - indexStartupCost;

 

       //估算表元组数:基于索引的选择率与元组个数的乘积是能取到的元组个数

       tuples_fetched
= clamp_row_est(indexSelectivity * baserel->tuples);

 

       //从表空间取数据页的IO花费:随机读取的花费、顺序读取的花费

       get_tablespace_page_costs(baserel->reltablespace,

                                                   &spc_random_page_cost,

                                                   &spc_seq_page_cost);

 

       //根据索引和索引使用次数,在区分“索引扫描”和“只索引扫描”方式下,计算索引页的IO花费

       if
(loop_count > 1)

       {

              //最大IO计算方式:

              //计算依据:元组数、循环次数、基表页面数、索引页面数

              pages_fetched
= index_pages_fetched(tuples_fetched * loop_count,

                                          baserel->pages,
(double) index->pages, root);

              //如果是“只索引扫描”,则重新计算页面获取数(因为不需要做表的扫描,只扫描索引的页面即可)

              if
(indexonly)

                     pages_fetched
= ceil(pages_fetched * (1.0 - baserel->allvisfrac));

              max_IO_cost
= (pages_fetched * spc_random_page_cost) / loop_count;

 

              //最小IO计算方式:

              //考虑了索引存在选择率的问题

              //计算依据:索引选择率、元组数、循环次数、基表页面数、索引页面数

              pages_fetched
= ceil(indexSelectivity
* (double) baserel->pages);

              pages_fetched
= index_pages_fetched(pages_fetched * loop_count,

                                          baserel->pages,
(double) index->pages, root);

//如果是“只索引扫描”,则重新计算页面获取数

              if
(indexonly)

                     pages_fetched
= ceil(pages_fetched * (1.0 - baserel->allvisfrac));

              min_IO_cost
= (pages_fetched * spc_random_page_cost) / loop_count;

       }

       else
//道理同上,只是不需要考虑循环次数

       {……}

 

       csquared
= indexCorrelation * indexCorrelation;

       run_cost
+= max_IO_cost + csquared * (min_IO_cost - max_IO_cost);

 

       //估算每个元组的CPU花费赋值给qpqual_cost

       cost_qual_eval(&qpqual_cost,
list_difference_ptr(allclauses, path->indexquals), root);

                              

    //计算每个元组的CPU花费

       startup_cost
+= qpqual_cost.startup;

       cpu_per_tuple
= cpu_tuple_cost + qpqual_cost.per_tuple;

 

       run_cost
+= cpu_per_tuple * tuples_fetched;//计算获取所有元组的CPU花费

 

       path->path.startup_cost
= startup_cost;

       path->path.total_cost
= startup_cost + run_cost;

       //最后的总的花费是:索引和基表限制条件的启动花费+总的IO花费(主要是索引页的IO花费)+总的CPU花费(元组)

}

 

调用关系图解:


 

1.        
cost_index函数被create_index_path函数调用,完成索引扫描的花费估算;

2.        
cost_index函数被reparameterize_path函数调用,完成参数化路径的索引扫描的花费估算。

 

1.1.1.2     Tid扫描的代价估算:cost_tidscan

函数功能:

计算一个关系使用Tid扫描方式的花费。

 

代码分析:

Void cost_tidscan(Path
*path, PlannerInfo *root, RelOptInfo *baserel, List *tidquals)

{

……

       //计算期望得到的元组个数(ntuples),分三种情况处理,tid分别是ScalarArrayOpExpr、CurrentOfExpr、CTID
= something这三种格式

……

       //计算Tid条件的花费(值计算一次,其他的限制条件的花费需要为每个元组都计算一次)

       cost_qual_eval(&tid_qual_cost,
tidquals, root);

//从表空间取数据页的IO花费:随机读取的花费

get_tablespace_page_costs(baserel->reltablespace, &spc_random_page_cost, NULL);

       //计算元组获取的IO花费

       run_cost
+= spc_random_page_cost * ntuples;

 

       //计算元组获取的CPU花费

       startup_cost
+= baserel->baserestrictcost.startup +

              tid_qual_cost.per_tuple;

       cpu_per_tuple
= cpu_tuple_cost + baserel->baserestrictcost.per_tuple -

              tid_qual_cost.per_tuple;

       run_cost
+= cpu_per_tuple * ntuples;

 

       path->startup_cost
= startup_cost;

       path->total_cost
= startup_cost + run_cost;

       //最后的总的花费是:基表限制条件的启动花费(包括tid条件花费)+总的IO花费+总的CPU花费

}

 

调用关系图解:


1.        
cost_tidscan函数被create_tidscan_path函数调用,直至被set_plain_rel_pathlist函数调用,用以完成单表tid扫描的花费估算。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: