文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

PostgreSQL中set_base_rel_sizes函数及其子函数案例分析

2024-04-02 19:55

关注

本篇内容介绍了“PostgreSQL中set_base_rel_sizes函数及其子函数案例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

make_one_rel源代码:

 RelOptInfo *
 make_one_rel(PlannerInfo *root, List *joinlist)
 {
     RelOptInfo *rel;
     Index       rti;
 
     
     root->all_baserels = NULL;
     for (rti = 1; rti < root->simple_rel_array_size; rti++)//遍历RelOptInfo
     {
         RelOptInfo *brel = root->simple_rel_array[rti];
 
         
         if (brel == NULL)
             continue;
 
         Assert(brel->relid == rti); 
 
         
         if (brel->reloptkind != RELOPT_BASEREL)
             continue;
 
         root->all_baserels = bms_add_member(root->all_baserels, brel->relid);//添加到all_baserels遍历中
     }
 
     
     //设置RelOptInfo的consider_param_startup变量,是否考量fast-start plans
     set_base_rel_consider_startup(root);
 
     
     set_base_rel_sizes(root);//估算Relation的Size并且设置consider_parallel标记
     set_base_rel_pathlists(root);//生成Relation的扫描(访问)路径
 
     
     rel = make_rel_from_joinlist(root, joinlist);
 
     
     Assert(bms_equal(rel->relids, root->all_baserels));
   //返回最上层的RelOptInfo
     return rel;
 }

一、数据结构

RelOptInfo

 typedef struct RelOptInfo
 {
     NodeTag     type;//节点标识
 
     RelOptKind  reloptkind;//RelOpt类型
 
     
     Relids      relids;         
 
     
     double      rows;           
 
     
     bool        consider_startup;   
     bool        consider_param_startup; 
     bool        consider_parallel;  
 
     
     struct PathTarget *reltarget;   
 
     
     List       *pathlist;       
     List       *ppilist;        
     List       *partial_pathlist;   
     struct Path *cheapest_startup_path;//代价最低的启动路径
     struct Path *cheapest_total_path;//代价最低的整体路径
     struct Path *cheapest_unique_path;//代价最低的获取唯一值的路径
     List       *cheapest_parameterized_paths;//代价最低的参数化?路径链表
 
     
     
     Relids      direct_lateral_relids;  
     Relids      lateral_relids; 
 
     
     //reloptkind=RELOPT_BASEREL时使用的数据结构
     Index       relid;          
     Oid         reltablespace;  
     RTEKind     rtekind;        
     AttrNumber  min_attr;       
     AttrNumber  max_attr;       
     Relids     *attr_needed;    
     int32      *attr_widths;    
     List       *lateral_vars;   
     Relids      lateral_referencers;    
     List       *indexlist;      
     List       *statlist;       
     BlockNumber pages;          
     double      tuples;         
     double      allvisfrac;     
     PlannerInfo *subroot;       
     List       *subplan_params; 
     int         rel_parallel_workers;   
 
     
     //FWD相关信息
     Oid         serverid;       
     Oid         userid;         
     bool        useridiscurrent;    
     
     struct FdwRoutine *fdwroutine;
     void       *fdw_private;
 
     
     //已知的,可保证唯一的Relids链表
     List       *unique_for_rels;    
     List       *non_unique_for_rels;    
 
     
     List       *baserestrictinfo;   
     QualCost    baserestrictcost;   
     Index       baserestrict_min_security;  
     List       *joininfo;       
     bool        has_eclass_joins;   
 
     
     bool        consider_partitionwise_join;    
     Relids      top_parent_relids;  
 
     
     //分区表使用
     PartitionScheme part_scheme;    
     int         nparts;         
     struct PartitionBoundInfoData *boundinfo;   
     List       *partition_qual; 
     struct RelOptInfo **part_rels;  
     List      **partexprs;      
     List      **nullable_partexprs; 
     List       *partitioned_child_rels; 
 } RelOptInfo;

二、源码解读

make_one_rel函数调用了set_base_rel_sizes,该函数的主要实现逻辑通过调用set_rel_size实现.现重点考察函数set_rel_size中对基础关系进行估算的处理逻辑,即函数set_plain_rel_size的实现逻辑.
set_plain_rel_size

 
 static void
 set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 {
     
     check_index_predicates(root, rel);//验证部分(条件)索引的可用性
 
     
     set_baserel_size_estimates(root, rel);//标记rel的输出行数/行宽等信息
 }

set_plain_rel_size->check_index_predicates
如果部分(条件)索引的谓词与查询语句相匹配,则predOK设置为true
比如:
数据表t1(c1 int,c2 varchar(40),...),如存在索引idx_t1_partial_c1,条件为where c1 > 100
查询条件为where c1 > 100(是否支持>200?),那么该谓词与查询条件相匹配

//----------------------------------------- check_index_predicates
 

 void
 check_index_predicates(PlannerInfo *root, RelOptInfo *rel)
 {
     List       *clauselist;//条件链表
     bool        have_partial;//是否包含部分索引
     bool        is_target_rel;//目标rel?
     Relids      otherrels;//Relids
     ListCell   *lc;//临时变量
 
     
     Assert(IS_SIMPLE_REL(rel));//rel必须是基础关系
 
     
     have_partial = false;
     foreach(lc, rel->indexlist)//遍历index
     {
         IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);
 
         index->indrestrictinfo = rel->baserestrictinfo;//设置索引约束条件
         if (index->indpred)
             have_partial = true;//存在部分索引
     }
     if (!have_partial)
         return;
 
     
     clauselist = list_copy(rel->baserestrictinfo);//条件语句初始化
 
     
     foreach(lc, rel->joininfo)//遍历连接条件
     {
         RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);//条件
 
         
         if (!join_clause_is_movable_to(rinfo, rel))//条件是否可以下推到Rel中
             continue;//不可用,下一个条件
 
         clauselist = lappend(clauselist, rinfo);//可以,则添加到条件语句链表中
     }
 
     
     if (rel->reloptkind == RELOPT_OTHER_MEMBER_REL)
         otherrels = bms_difference(root->all_baserels,
                                    find_childrel_parents(root, rel));//
     else
         otherrels = bms_difference(root->all_baserels, rel->relids);//获取与rel无关的其他rels
 
     if (!bms_is_empty(otherrels))
         clauselist =
             list_concat(clauselist,
                         generate_join_implied_equalities(root,
                                                          bms_union(rel->relids,
                                                                    otherrels),
                                                          otherrels,
                                                          rel));//添加到条件语句
 
     
     is_target_rel = (rel->relid == root->parse->resultRelation ||
                      get_plan_rowmark(root->rowMarks, rel->relid) != NULL);
 
     
     foreach(lc, rel->indexlist)//遍历index
     {
         IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);
         ListCell   *lcr;
 
         if (index->indpred == NIL)
             continue;           
 
         if (!index->predOK)     
             index->predOK = predicate_implied_by(index->indpred, clauselist,
                                                  false);//设置predOK参数
 
         
         if (is_target_rel)
             continue;
 
         
         index->indrestrictinfo = NIL;
         foreach(lcr, rel->baserestrictinfo)
         {
             RestrictInfo *rinfo = (RestrictInfo *) lfirst(lcr);
 
             
             if (contain_mutable_functions((Node *) rinfo->clause) ||
                 !predicate_implied_by(list_make1(rinfo->clause),
                                       index->indpred, false))
                 index->indrestrictinfo = lappend(index->indrestrictinfo, rinfo);//
         }
     }
 }

set_plain_rel_size->set_baserel_size_estimates
函数注释:

//----------------------------------------- set_baserel_size_estimates
 

源代码:

 void
 set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel)
 {
     double      nrows;
 
     
     Assert(rel->relid > 0);
 
     nrows = rel->tuples *
         clauselist_selectivity(root,
                                rel->baserestrictinfo,
                                0,
                                JOIN_INNER,
                                NULL);//元组总数*选择率
 
     rel->rows = clamp_row_est(nrows);
 
     cost_qual_eval(&rel->baserestrictcost, rel->baserestrictinfo, root);
 
     set_rel_width(root, rel);
 }

//----------------------------------------- clauselist_selectivity

 
 Selectivity
 clauselist_selectivity(PlannerInfo *root,
                        List *clauses,
                        int varRelid,
                        JoinType jointype,
                        SpecialJoinInfo *sjinfo)
 {
     Selectivity s1 = 1.0;//默认返回值
     RelOptInfo *rel;//
     Bitmapset  *estimatedclauses = NULL;//位图集合
     RangeQueryClause *rqlist = NULL;//范围查询语句
     ListCell   *l;//临时变量
     int         listidx;
 
     
     if (list_length(clauses) == 1)
         return clause_selectivity(root, (Node *) linitial(clauses),
                                   varRelid, jointype, sjinfo);//单个条件
 
     
     //如果条件链表中的元素依赖的rel有且只有一个,则返回此rel
     rel = find_single_rel_for_clauses(root, clauses);
     //应用dependencies_clauselist_selectivity中的可用的条件进行选择率估算
     if (rel && rel->rtekind == RTE_RELATION && rel->statlist != NIL)
     {
         
         s1 *= dependencies_clauselist_selectivity(root, clauses, varRelid,
                                                   jointype, sjinfo, rel,
                                                   &estimatedclauses);
 
         
     }
 
     
     listidx = -1;
     foreach(l, clauses)//遍历
     {
         Node       *clause = (Node *) lfirst(l);//链表中的元素
         RestrictInfo *rinfo;
         Selectivity s2;
 
         listidx++;
 
         
         if (bms_is_member(listidx, estimatedclauses))//跳过已处理的条件
             continue;
 
         
         s2 = clause_selectivity(root, clause, varRelid, jointype, sjinfo);//获取条件选择率
 
         
         if (IsA(clause, RestrictInfo))//条件语句是RestrictInfo类型
         {
             rinfo = (RestrictInfo *) clause;
             if (rinfo->pseudoconstant)//常量
             {
                 s1 = s1 * s2;//直接相乘
                 continue;
             }
             clause = (Node *) rinfo->clause;//条件表达式
         }
         else
             rinfo = NULL;//不是RestrictInfo类型,rinfo设置为NULL
 
         
         if (is_opclause(clause) && list_length(((OpExpr *) clause)->args) == 2)//OpExpr
         {
             OpExpr     *expr = (OpExpr *) clause;//条件语句
             bool        varonleft = true;
             bool        ok;
 
             if (rinfo)//rinfo中的条件语句
             {
                 ok = (bms_membership(rinfo->clause_relids) == BMS_SINGLETON) &&
                     (is_pseudo_constant_clause_relids(lsecond(expr->args),
                                                       rinfo->right_relids) ||
                      (varonleft = false,
                       is_pseudo_constant_clause_relids(linitial(expr->args),
                                                        rinfo->left_relids)));
             }
             else//裸条件语句
             {
                 ok = (NumRelids(clause) == 1) &&
                     (is_pseudo_constant_clause(lsecond(expr->args)) ||
                      (varonleft = false,
                       is_pseudo_constant_clause(linitial(expr->args))));
             }
 
             if (ok)//校验通过
             {
                 
                 switch (get_oprrest(expr->opno))
                 {
                     case F_SCALARLTSEL:
                     case F_SCALARLESEL:
                         addRangeClause(&rqlist, clause,
                                        varonleft, true, s2);//范围条件
                         break;
                     case F_SCALARGTSEL:
                     case F_SCALARGESEL:
                         addRangeClause(&rqlist, clause,
                                        varonleft, false, s2);//范围条件
                         break;
                     default:
                         
                         s1 = s1 * s2;//直接相乘
                         break;
                 }
                 continue;       
             }
         }
 
         
         s1 = s1 * s2;//直接相乘
     }
 
     
     while (rqlist != NULL)//处理范围条件
     {
         RangeQueryClause *rqnext;
 
         if (rqlist->have_lobound && rqlist->have_hibound)//存在上下限
         {
             
             Selectivity s2;//选择率
 
             
             if (rqlist->hibound == DEFAULT_INEQ_SEL ||
                 rqlist->lobound == DEFAULT_INEQ_SEL)//默认值
             {
                 s2 = DEFAULT_RANGE_INEQ_SEL;//默认为DEFAULT_RANGE_INEQ_SEL
             }
             else
             {
                 s2 = rqlist->hibound + rqlist->lobound - 1.0;//计算公式在注释已解释
 
                 
                 s2 += nulltestsel(root, IS_NULL, rqlist->var,
                                   varRelid, jointype, sjinfo);//NULL值
 
                 
                 if (s2 <= 0.0)//小于0?
                 {
                     if (s2 < -0.01)
                     {
                         
                         s2 = DEFAULT_RANGE_INEQ_SEL;//小于﹣1%,默认值
                     }
                     else
                     {
                         
                         s2 = 1.0e-10;,//否则设置为1的﹣10次方
                     }
                 }
             }
             
             s1 *= s2;//直接相乘
         }
         else//只有其中一个限制
         {
             
             if (rqlist->have_lobound)
                 s1 *= rqlist->lobound;//下限
             else
                 s1 *= rqlist->hibound;//上限
         }
         
         rqnext = rqlist->next;//释放资源
         pfree(rqlist);
         rqlist = rqnext;
     }
 
     return s1;//返回选择率
 }

 
 Selectivity
 clause_selectivity(PlannerInfo *root,
                    Node *clause,
                    int varRelid,
                    JoinType jointype,
                    SpecialJoinInfo *sjinfo)
 {
     Selectivity s1 = 0.5;       
     RestrictInfo *rinfo = NULL;
     bool        cacheable = false;
 
     if (clause == NULL)         
         return s1;
 
     if (IsA(clause, RestrictInfo))//RestrictInfo
     {
         rinfo = (RestrictInfo *) clause;
 
         
         if (rinfo->pseudoconstant)//pseudoconstant,不影响选择率
         {
             if (!IsA(rinfo->clause, Const))
                 return (Selectivity) 1.0;//返回1.0
         }
 
         
         if (rinfo->norm_selec > 1)
             return (Selectivity) 1.0;//返回1.0
 
         
         if (varRelid == 0 ||
             bms_is_subset_singleton(rinfo->clause_relids, varRelid))//varRelid为0
         {
             
             if (jointype == JOIN_INNER)
             {
                 if (rinfo->norm_selec >= 0)
                     return rinfo->norm_selec;
             }
             else
             {
                 if (rinfo->outer_selec >= 0)
                     return rinfo->outer_selec;
             }
             cacheable = true;
         }
 
         
         if (rinfo->orclause)
             clause = (Node *) rinfo->orclause;
         else
             clause = (Node *) rinfo->clause;
     }
 
     if (IsA(clause, Var))//Var
     {
         Var        *var = (Var *) clause;
 
         
         if (var->varlevelsup == 0 &&
             (varRelid == 0 || varRelid == (int) var->varno))
         {
             
             s1 = boolvarsel(root, (Node *) var, varRelid);//bool Var选择率
         }
     }
     else if (IsA(clause, Const))//常量
     {
         
         Const      *con = (Const *) clause;
 
         s1 = con->constisnull ? 0.0 :
             DatumGetBool(con->constvalue) ? 1.0 : 0.0;//常量有效则为1.0,否则为0.0
     }
     else if (IsA(clause, Param))//参数
     {
         
         Node       *subst = estimate_expression_value(root, clause);
 
         if (IsA(subst, Const))
         {
             
             Const      *con = (Const *) subst;
 
             s1 = con->constisnull ? 0.0 :
                 DatumGetBool(con->constvalue) ? 1.0 : 0.0;
         }
         else
         {
             
         }
     }
     else if (not_clause(clause))//反选
     {
         
         s1 = 1.0 - clause_selectivity(root,
                                       (Node *) get_notclausearg((Expr *) clause),
                                       varRelid,
                                       jointype,
                                       sjinfo);
     }
     else if (and_clause(clause))//AND语句
     {
         
         s1 = clauselist_selectivity(root,
                                     ((BoolExpr *) clause)->args,
                                     varRelid,
                                     jointype,
                                     sjinfo);//递归调用
     }
     else if (or_clause(clause))//OR语句
     {
         
         ListCell   *arg;
 
         s1 = 0.0;
         foreach(arg, ((BoolExpr *) clause)->args)
         {
             Selectivity s2 = clause_selectivity(root,
                                                 (Node *) lfirst(arg),
                                                 varRelid,
                                                 jointype,
                                                 sjinfo);//递归调用
 
             s1 = s1 + s2 - s1 * s2;
         }
     }
     else if (is_opclause(clause) || IsA(clause, DistinctExpr))
     {
         OpExpr     *opclause = (OpExpr *) clause;
         Oid         opno = opclause->opno;
 
         if (treat_as_join_clause(clause, rinfo, varRelid, sjinfo))
         {
             
             s1 = join_selectivity(root, opno,
                                   opclause->args,
                                   opclause->inputcollid,
                                   jointype,
                                   sjinfo);//连接条件
         }
         else
         {
             
             s1 = restriction_selectivity(root, opno,
                                          opclause->args,
                                          opclause->inputcollid,
                                          varRelid);//限制条件
         }
 
         
         if (IsA(clause, DistinctExpr))
             s1 = 1.0 - s1;//Distinct?
     }
     else if (IsA(clause, ScalarArrayOpExpr))//数组
     {
         
         s1 = scalararraysel(root,
                             (ScalarArrayOpExpr *) clause,
                             treat_as_join_clause(clause, rinfo,
                                                  varRelid, sjinfo),
                             varRelid,
                             jointype,
                             sjinfo);
     }
     else if (IsA(clause, RowCompareExpr))//行对比
     {
         
         s1 = rowcomparesel(root,
                            (RowCompareExpr *) clause,
                            varRelid,
                            jointype,
                            sjinfo);
     }
     else if (IsA(clause, NullTest))//NullTest
     {
         
         s1 = nulltestsel(root,
                          ((NullTest *) clause)->nulltesttype,
                          (Node *) ((NullTest *) clause)->arg,
                          varRelid,
                          jointype,
                          sjinfo);
     }
     else if (IsA(clause, BooleanTest))//BooleanTest
     {
         
         s1 = booltestsel(root,
                          ((BooleanTest *) clause)->booltesttype,
                          (Node *) ((BooleanTest *) clause)->arg,
                          varRelid,
                          jointype,
                          sjinfo);
     }
     else if (IsA(clause, CurrentOfExpr))//CurrentOfExpr
     {
         
         CurrentOfExpr *cexpr = (CurrentOfExpr *) clause;
         RelOptInfo *crel = find_base_rel(root, cexpr->cvarno);
 
         if (crel->tuples > 0)
             s1 = 1.0 / crel->tuples;
     }
     else if (IsA(clause, RelabelType))//RelabelType
     {
         
         s1 = clause_selectivity(root,
                                 (Node *) ((RelabelType *) clause)->arg,
                                 varRelid,
                                 jointype,
                                 sjinfo);
     }
     else if (IsA(clause, CoerceToDomain))//CoerceToDomain
     {
         
         s1 = clause_selectivity(root,
                                 (Node *) ((CoerceToDomain *) clause)->arg,
                                 varRelid,
                                 jointype,
                                 sjinfo);
     }
     else
     {
         
         s1 = boolvarsel(root, clause, varRelid);//默认为bool Var
     }
 
     
     if (cacheable)//缓存?
     {
         if (jointype == JOIN_INNER)
             rinfo->norm_selec = s1;
         else
             rinfo->outer_selec = s1;
     }
 
 #ifdef SELECTIVITY_DEBUG
     elog(DEBUG4, "clause_selectivity: s1 %f", s1);
 #endif                          
 
     return s1;
 }

 
 static RelOptInfo *
 find_single_rel_for_clauses(PlannerInfo *root, List *clauses)
 {
     int         lastrelid = 0;
     ListCell   *l;
 
     foreach(l, clauses)
     {
         RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
         int         relid;
 
         
         if (!IsA(rinfo, RestrictInfo))
             return NULL;
 
         if (bms_is_empty(rinfo->clause_relids))
             continue;           
         if (!bms_get_singleton_member(rinfo->clause_relids, &relid))
             return NULL;        
         if (lastrelid == 0)
             lastrelid = relid;  
         else if (relid != lastrelid)
             return NULL;        
     }
 
     if (lastrelid != 0)
         return find_base_rel(root, lastrelid);
 
     return NULL;                
 }

//---------------------------------------------- clamp_row_est
 
 double
 clamp_row_est(double nrows)
 {
     
     if (nrows <= 1.0)
         nrows = 1.0;//小于1,返回1
     else
         nrows = rint(nrows);//整型
 
     return nrows;
 }
 
//---------------------------------------------- cost_qual_eval

 
 void
 cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root)
 {
     cost_qual_eval_context context;
     ListCell   *l;
 
     context.root = root;
     context.total.startup = 0;
     context.total.per_tuple = 0;
 
     
 
     foreach(l, quals)
     {
         Node       *qual = (Node *) lfirst(l);
 
         cost_qual_eval_walker(qual, &context);
     }
 
     *cost = context.total;
 }


//---------------------------------------------- set_rel_width

 
 static void
 set_rel_width(PlannerInfo *root, RelOptInfo *rel)
 {
     Oid         reloid = planner_rt_fetch(rel->relid, root)->relid;
     int32       tuple_width = 0;
     bool        have_wholerow_var = false;
     ListCell   *lc;
 
     
     rel->reltarget->cost.startup = 0;
     rel->reltarget->cost.per_tuple = 0;
 
     foreach(lc, rel->reltarget->exprs)
     {
         Node       *node = (Node *) lfirst(lc);
 
         
         if (IsA(node, Var) &&
             ((Var *) node)->varno == rel->relid)
         {
             Var        *var = (Var *) node;
             int         ndx;
             int32       item_width;
 
             Assert(var->varattno >= rel->min_attr);
             Assert(var->varattno <= rel->max_attr);
 
             ndx = var->varattno - rel->min_attr;
 
             
             if (var->varattno == 0)
             {
                 have_wholerow_var = true;
                 continue;
             }
 
             
             if (rel->attr_widths[ndx] > 0)
             {
                 tuple_width += rel->attr_widths[ndx];
                 continue;
             }
 
             
             if (reloid != InvalidOid && var->varattno > 0)
             {
                 item_width = get_attavgwidth(reloid, var->varattno);
                 if (item_width > 0)
                 {
                     rel->attr_widths[ndx] = item_width;
                     tuple_width += item_width;
                     continue;
                 }
             }
 
             
             item_width = get_typavgwidth(var->vartype, var->vartypmod);
             Assert(item_width > 0);
             rel->attr_widths[ndx] = item_width;
             tuple_width += item_width;
         }
         else if (IsA(node, PlaceHolderVar))
         {
             
             PlaceHolderVar *phv = (PlaceHolderVar *) node;
             PlaceHolderInfo *phinfo = find_placeholder_info(root, phv, false);
             QualCost    cost;
 
             tuple_width += phinfo->ph_width;
             cost_qual_eval_node(&cost, (Node *) phv->phexpr, root);
             rel->reltarget->cost.startup += cost.startup;
             rel->reltarget->cost.per_tuple += cost.per_tuple;
         }
         else
         {
             
             int32       item_width;
             QualCost    cost;
 
             item_width = get_typavgwidth(exprType(node), exprTypmod(node));
             Assert(item_width > 0);
             tuple_width += item_width;
             
             cost_qual_eval_node(&cost, node, root);
             rel->reltarget->cost.startup += cost.startup;
             rel->reltarget->cost.per_tuple += cost.per_tuple;
         }
     }
 
     
     if (have_wholerow_var)
     {
         int32       wholerow_width = MAXALIGN(SizeofHeapTupleHeader);
 
         if (reloid != InvalidOid)
         {
             
             wholerow_width += get_relation_data_width(reloid,
                                                       rel->attr_widths - rel->min_attr);
         }
         else
         {
             
             AttrNumber  i;
 
             for (i = 1; i <= rel->max_attr; i++)
                 wholerow_width += rel->attr_widths[i - rel->min_attr];
         }
 
         rel->attr_widths[0 - rel->min_attr] = wholerow_width;
 
         
         tuple_width += wholerow_width;
     }
 
     Assert(tuple_width >= 0);
     rel->reltarget->width = tuple_width;
 }

三、跟踪分析

测试脚本:
单位信息表,插入100,000行数据,dwbh为主键,同时创建函数索引和部分索引

drop table if exists t_dwxx;
create table t_dwxx(dwmc varchar(100),dwbh varchar(20),dwdz varchar(100));
alter table t_dwxx add primary key(dwbh);
create index idx_dwxx_expr on t_dwxx(trim(dwmc));
create index idx_dwxx_predicate_dwbh on t_dwxx(dwbh) where dwbh > '50000';

truncate table t_dwxx;
insert into t_dwxx(dwmc,dwbh,dwdz) 
select 'DWMC'||generate_series(1,100000),generate_series(1,100000)||'','DWDZ'||generate_series(1,100000);

个人信息表,插入5,000,000行数据,在grbh和dwbh上创建索引

drop table if exists t_grxx;
create table t_grxx(dwbh varchar(10),grbh varchar(10),xm varchar(20),xb varchar(10),nl int);

insert into t_grxx(dwbh,grbh,xm,xb,nl) 
select generate_series(1,5000000)/50||'',generate_series(1,5000000),'XM'||generate_series(1,5000000),
(case when (floor(random()*2)=0) then '男' else '女' end),floor(random() * 100 + 1)::int;

create index idx_t_grxx_grbh on t_grxx(grbh);
create index idx_t_dwxx_grbh on t_grxx(dwbh);

个人缴费信息表,在grbh上创建索引,多次插入5,000,000行数据

drop table if exists t_jfxx;
create table t_jfxx(grbh varchar(10),ny varchar(10),je float);
-- 根据实际情况,多次执行以下SQL
insert into t_jfxx(grbh,ny,je) 
select generate_series(1,5000000),
       to_char(now()::timestamp - (floor(random()*240+1)||' month')::interval,'yyyymm'),
       floor(random()*10000+1);

create index idx_t_jfxx_grbh on t_jfxx(grbh);

执行简单的查询SQL:

select t1.* from t_dwxx t1 where dwbh > '60000' and dwbh < '70000' and dwbh < '65000';

执行计划如下:

testdb=# explain (analyze true,verbose) select t1.* from t_dwxx t1 where dwbh > '60000' and dwbh < '70000' and dwbh < '65000';
                                                               QUERY PLAN                                                    
           
-----------------------------------------------------------------------------------------------------------------------------
-----------
 Bitmap Heap Scan on public.t_dwxx t1  (cost=134.19..956.12 rows=5482 width=23) (actual time=1.484..2.744 rows=5554 loops=1)
   Output: dwmc, dwbh, dwdz
   Recheck Cond: (((t1.dwbh)::text > '60000'::text) AND ((t1.dwbh)::text < '70000'::text) AND ((t1.dwbh)::text < '65000'::tex
t))
   Heap Blocks: exact=45
   ->  Bitmap Index Scan on idx_dwxx_predicate_dwbh  (cost=0.00..132.81 rows=5482 width=0) (actual time=1.467..1.467 rows=555
4 loops=1)
         Index Cond: (((t1.dwbh)::text > '60000'::text) AND ((t1.dwbh)::text < '70000'::text) AND ((t1.dwbh)::text < '65000':
:text))
 Planning Time: 0.204 ms
 Execution Time: 3.288 ms

启动gdb跟踪分析:

(gdb) b set_baserel_size_estimates
Breakpoint 1 at 0x747bf5: file costsize.c, line 4302.
(gdb) c
Continuing.

Breakpoint 1, set_baserel_size_estimates (root=0x2686fa8, rel=0x26873b8) at costsize.c:4302
4302    nrows = rel->tuples *

进入函数clauselist_selectivity:

(gdb) step
clauselist_selectivity (root=0x2686fa8, clauses=0x271f600, varRelid=0, jointype=JOIN_INNER, sjinfo=0x0) at clausesel.c:105
105   Selectivity s1 = 1.0;
...
124   rel = find_single_rel_for_clauses(root, clauses);
(gdb) 
125   if (rel && rel->rtekind == RTE_RELATION && rel->statlist != NIL)
#与限制条件相关的rel(t_dwxx)
(gdb) p *rel
$1 = {type = T_RelOptInfo, reloptkind = RELOPT_BASEREL, relids = 0x2687728, rows = 0, consider_startup = false, 
  consider_param_startup = false, consider_parallel = true, reltarget = 0x271e228, pathlist = 0x0, ppilist = 0x0, 
  partial_pathlist = 0x0, cheapest_startup_path = 0x0, cheapest_total_path = 0x0, cheapest_unique_path = 0x0, 
  cheapest_parameterized_paths = 0x0, direct_lateral_relids = 0x0, lateral_relids = 0x0, relid = 1, reltablespace = 0, 
  rtekind = RTE_RELATION, min_attr = -7, max_attr = 3, attr_needed = 0x271e278, attr_widths = 0x271e308, 
  lateral_vars = 0x0, lateral_referencers = 0x0, indexlist = 0x271e700, statlist = 0x0, pages = 726, tuples = 100000, 
  allvisfrac = 0, subroot = 0x0, subplan_params = 0x0, rel_parallel_workers = -1, serverid = 0, userid = 0, 
  useridiscurrent = false, fdwroutine = 0x0, fdw_private = 0x0, unique_for_rels = 0x0, non_unique_for_rels = 0x0, 
  baserestrictinfo = 0x271f600, baserestrictcost = {startup = 0, per_tuple = 0}, baserestrict_min_security = 0, 
  joininfo = 0x0, has_eclass_joins = false, top_parent_relids = 0x0, part_scheme = 0x0, nparts = 0, boundinfo = 0x0, 
  partition_qual = 0x0, part_rels = 0x0, partexprs = 0x0, nullable_partexprs = 0x0, partitioned_child_rels = 0x0}

开始循环处理:

152   foreach(l, clauses)
...

第一个条件语句,调用clause_selectivity后,选择率为0.44...

168     s2 = clause_selectivity(root, clause, varRelid, jointype, sjinfo);
(gdb) 
176     if (IsA(clause, RestrictInfo))
(gdb) p s2
$2 = 0.44045086705202319

添加到范围条件语句中:

...
225         switch (get_oprrest(expr->opno))
(gdb) 
234             addRangeClause(&rqlist, clause,
(gdb) 
236             break;

第二个条件语句,调用clause_selectivity后,选择率为0.66...,,同样会添加到范围条件语句中:

168     s2 = clause_selectivity(root, clause, varRelid, jointype, sjinfo);
(gdb) 
176     if (IsA(clause, RestrictInfo))
(gdb) p s2
$3 = 0.66904390539053915
...
225         switch (get_oprrest(expr->opno))
(gdb) 
229             addRangeClause(&rqlist, clause,

第三个条件语句,调用clause_selectivity后,选择率为0.61...,,同样会添加到范围条件语句中:

168     s2 = clause_selectivity(root, clause, varRelid, jointype, sjinfo);
(gdb) 
176     if (IsA(clause, RestrictInfo))
(gdb) p s2
$4 = 0.61437297872340435
...
225         switch (get_oprrest(expr->opno))
(gdb) 
229             addRangeClause(&rqlist, clause,

结束循环,开始处理范围条件语句:

253   while (rqlist != NULL)
(gdb) n
#既有上限,也有下限
(gdb) p *rqlist
$7 = {next = 0x0, var = 0x271dba8, have_lobound = true, have_hibound = true, lobound = 0.44045086705202319, 
  hibound = 0.61437297872340435}
...
#计算公式注释已作介绍
(gdb) n
274         s2 = rqlist->hibound + rqlist->lobound - 1.0;
(gdb) 
277         s2 += nulltestsel(root, IS_NULL, rqlist->var,
#最终结果  
(gdb) 
325   return s1;
(gdb) p s1
$11 = 0.054823845775427538
...

回到主函数:

(gdb) 
set_baserel_size_estimates (root=0x2686fa8, rel=0x26873b8) at costsize.c:4302
4302    nrows = rel->tuples *
(gdb) 
4309    rel->rows = clamp_row_est(nrows);
(gdb) 
4311    cost_qual_eval(&rel->baserestrictcost, rel->baserestrictinfo, root);
(gdb) 
4313    set_rel_width(root, rel);
(gdb) p rel->rows
$12 = 5482

结果为5482,执行计划中的rows=5482就是这么来的.

“PostgreSQL中set_base_rel_sizes函数及其子函数案例分析”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-数据库
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯