PostgreSQL中query_planner函数的处理逻辑分析

分类:编程技术 时间:2024-02-20 15:57 浏览:0 评论:0
0
本文主要介绍《PostgreSQL中query_planner函数的处理逻辑分析》。在日常操作中,相信很多人对PostgreSQL中query_planner函数的处理逻辑分析存在疑问。小编查阅了各种资料,整理出了一个简单又好的解决方案。使用的操作方法,希望能帮助您解答《PostgreSQL中query_planner函数的逻辑分析》的疑惑!接下来就请跟随小编一起来学习吧!

1.重要数据结构

RelOptInfo
查询语句经过查询重写/表达式简化/外连接消除等处理后,查询树Query已经标准化,而查询树中的RangeTblEntry(RTE)数据结构已经完成了它的历史使命,因此可以进入逻辑优化处理。此阶段使用RelOptInfo数据结构。

 typedef enum RelOptKind { RELOPT_BASEREL,//基本关系(如基表/子查询等) RELOPT_JOINREL,//连接生成的关系。需要说明的是,连接产生的结果也可以视为关系。 RELOPT_OTHER_MEMBER_REL, RELOPT_OTHER_JOINREL, RELOPT_UPPER_REL,//上层关系 RELOPT_OTHER_UPPER_REL, RELOPT_DEADREL } RelOptKind; /* * 给定的关系是简单关系,即基本关系或“其他”成员关系吗? */ #define IS_SIMPLE_REL(rel) \ ((rel)->reloptkind == RELOPT_BASEREL || \ (rel)->reloptkind == RELOPT_OTHER_MEMBER_REL) /* 给定的关系是连接关系吗? */ #def ine IS_JOIN_REL(rel) \ ((rel)->reloptkind == RELOPT_JOINREL || \ (rel)->reloptkind == RELOPT_OTHER_JOINREL) /* 给定的关系是上层关系吗? */ #define IS_UPPER_REL(rel) \     ((rel )->reloptkind == RELOPT_UPPER_REL || \ (rel)->reloptkind == RELOPT_OTHER_UPPER_REL) /* 给定的关系是“其他”关系吗? */ #define IS_OTHER_REL(rel) \ ((rel)->reloptkind== RELOPT_OTHER_MEMBER_REL || \ (rel)->reloptkind == RELOPT_OTHER_JOINREL || (rel)->reloptkind == RELO PT_OTHER_UPPER_REL ) typedef struct RelOptInfo { NodeTag type;//节点标识 RelOptKind reloptkind;//RelOpt 类型 /* 本 RelOptInfo 包含的所有关系 */ Relids relids; /*R elids(rtindex) 一组基本 relids(范围表索引)*/   /* 由规划器生成的大小估计 */ double rows; /*估计结果元组的数量 */ /* 每个关系规划器控制标志 */ bool think_startup; /*你考虑启动成本吗?是的,需要保留启动成本低的路径并保留启动成本低的路径吗? */ 布尔考虑_param_startup; /*是否考虑参数化路径同上,对于参数化路径? */ 布尔考虑_并行; /*是否考虑并行处理路径考虑并行路径? */ /* 扫描此关系的路径的默认结果目标列表 */ struct PathTarget *reltarget; /*扫描此 Vars/Exprs, co 关系列表时的默认结果st, width */ /* 物化信息 */ List *pathlist; / *访问路径链表 Path 结构 */ List *ppilist; /*路径列表中使用的ParamPathInfos */ List *partial_pathlist; /* 部分路径 */ struct Path *cheapest_startup_path;//成本最低的启动路径 struct Path *cheapest_total_path;//成本最低的整体路径 struct Path *cheapest_unique_path;//获取唯一值的成本最低的路径 List *cheapest_parameterized_pa​​ths;//成本最低的参数化?路径链表 rels 和 join rels */ /* (另请参见 Lateral_vars 和 Lateral_referencers) */ Relids direct_ Lateral_relids; /*使用横向语法,将rels需要依赖的Relids直接横向引用 */ Relids Lateral_relids; /* rel 的最小参数化 */ /* 有关基本 rel 的信息(不是 setfor join rels!) */ // reloptkind=RELOPT_BASEREL 时使用的数据结构 Index relid; /* 关系 ID */ Oid reltablespace; /* 包含表空间的表空间 */ RTEKind rtekind;​​​​/* 基表?子查询?还是功能?等待?关系、子查询、函数等 */ AttrNumber min_attr; /* rel 的最小属性号最小 attrno (通常<0) */ AttrNumber max_attr; /* rel 的最大属性号最大 attrno */ Relids *attr_needed; /* 索引数组 [min_attr .. max_attr] */ int32 *attr_widths; /* 属性宽度数组索引 [min_attr .. max_attr] */ List * Lateral_vars; /* 关系依赖关系的 Vars/PHV LATERAL rel 引用的 Vars 和 PHV */ Relids Lateral_referencers; /*横向引用我的 Relids */ List *indexlist; /* IndexOptInfo 的 IndexOptInfo 列表 */ List *statlist; /* 统计信息列表list of StatisticExtInfo */ BlockNumberpages; /* 从 pg_class 派生的块号大小估计 */ double tuples; /* 元组数量 */ double allvisfrac; /* ? */ PlannerInfo *子根; /你需要多少工人?想要的并行工人数量*//*有关外国表和外国乔斯的信息*/// FWD 相关信息 oid Serverid;/*标识Server 用于fors表或join*/oid userid; /* 标识要检查访问权限的用户为 */ bool useidiscurrent; /* join 仅对当前用户有效 */ /* 使用“struct FdwRoutine”以避免在此处包含 fdwapi.h */ struct FdwRoutine *fdwroutine;无效*fdw_private; /* 用于记住的缓存空间,如果我们已经证明这个关系是唯一的 */ //已知的,保证唯一的 Relids 列表 List *unique_for_rels; / * 对于这些其他 relidssp 来说是唯一的;对于这些集合来说不是唯一的 */ /* 由各种扫描和连接使用: */ List *baserestrictinfo; /* 如果是基本关系,存储约束RestrictInfo结构(如果是base rel) */ QualCost baserestrictcost; /* 评估上述的成本 */ Index baserestrict_min_security; /* 最低安全级别 min security_level find inp, _eclass_joins; /* 是否存在等价类连接? T 表示 joininfo 不完整 */ /* 由分区连接使用: */ bool think_partitionwise_join; /* 党化?考虑分区 elids top_parent_relids; /* 最顶层父级的 Relids (if "other"           * rel) */ /* 用于分区关系 */ //分区表 use PartitionScheme part_scheme; /* 分区方案 分区方案。 */ int nparts; /* 分区数 分区数 */ struct PartitionBoundInfoData *boundinfo; /* 分区边界信息 Partitionbounds */ List *partition_qual; /* 分区约束 分区约束 */ struct RelOptInfo **part_rels; /* RelOptInfo 分区数组 分区 RelOptInfo 数组,                                                                                     **nullable_partexprs; /* 可空分区键表达式 可空分区键表达式。 */ 列表 *partitioned_child_rels; /* RT Indexes RT 索引列表。 */ } RelOptInfo;

PathCostComparison

 typedef enum { COSTs_equal,/*近似路径成本模糊相等*/Costs_better1,/*T第一路径成本较低。 Aper Than FIRST*/ Costs_ Different/*无论哪条路径,成本都没有占到优势。 * ResultPath 表示使用结果计划节点来计算没有基础表的无变量 * 目标列表(“SELECT 表达式”查询)。 * 查询也可以有一个 WHERE 子句,用“quals”表示。 * * 请注意,quals 是裸子句的列表,而不是 RestrictInfos。 */ typedef struct ResultPath //表示无基表的结果计划节点 { Path ; path;//扫描路径 List *quals;//where 语句表达式,裸子句,不包含 RestrictInfos } ResultPath; 

2.源代码解释

 /* * query_planner * 为基本查询生成路径(即简化的计划), * 这可能涉及连接,但不涉及任何更高级的功能。 * * 为基本查询(可能涉及连接)生成访问路径(也可以视为简化计划)。 * * 由于query_planner不处理顶层处理(grouping、*排序等)它无法自行选择最佳路径。相反,它 * 返回顶级连接的 RelOptInfo,并且调用者 * (grouping_planner) 可以在 rel 的幸存路径中进行选择。 * * query_planner不会处理顶层的处理(比如最后的分组/排序操作),因此,无法选择最优的访问路径 * 该函数会返回RelOptInfo到最高层的连接,grouping_planner可以在Select from其余路径 * root 描述要计划的查询 * tlist 是查询应生成的目标列表 * (这不一定是 root->parse->targetList!) * qp_callback 是一个在安全的情况下计算 query_pathkeys 的函数* q p_extra 是传递给 qp_callback 的可选额外数据 * * root 是计划信息/tlist 是投影列 * qp_callback 是计算 query_pathkeys 的函数/qp_extra 是传递给 qp_callback 的函数 * * 注意:PlannerInfo 节点也是包括一个 query_pathkeys 字段,它 * 告诉 query_planner 最终输出计划中所需的排序顺序。该值在调用时*不*可用,但在我们完成合并查询的等价类后由* qp_callback 计算。 * (在完成之前我们无法构造规范路径键。) */ RelOptInfo * query_ planner( PlannerInfo *root, List *tlist, query_pathkeys_callback qp_callback, void *qp_extra) { Query *parse = root->parse;//查询树列表 *加入列表; RelOptInfo *final_rel; //结果 Indexrti;//RTE 的索引 double ‘total_pages;//总页数 /* * 如果查询有一个空的连接树,那么就很简单 * "SELECT 2+2;"或“插入...值()”。很快就跌倒了。 */ if (parse->jointree->fromlist == NIL)//简单SQL,无FROM/WHERE语句{ = build_empty_join_rel(root);//创建返回结果/* * 如果一般查询允许并行,检查是否quals 是并行限制的。 (我们不需要检查final_rel->relttarget * 因为此时它是空的。 * 查询列表中的任何并行限制将稍后处理。)*/if (root-> Glob-> Parallelmodeok) // Pacific 模式? Final_rel->conside_parallel =   ;_parallel_safe (root, PARSE->Jointree->Quals);/*唯一的路径是一个简单的结果路径*/add_path (fath*) create_result_path(R.OOT, FINAL_REL, FINAL_REL->RELTARGET,(List*)解析-> Jointree-> Quals); // 添加访问路径/*选择最便宜的路径(在本例中非常简单...)*/set_cheapest (final_rel); // 选择最佳访问路径 // * * * 我们仍然需要调用 qp_callback,以防 * 类似于“SELECT 2+2 ORDER BY 1”。 */ root->canon_pathkeys = NIL; (*q p_callback) (root, qp_extra);//回调函数   return Final_rel;//返回                                                                                                                                                                                                                            。强>create_result_path

 /* * create_result_path * 创建表示 Result-and-nothing-else 计划的路径。 * * 这仅用于退化情况,例如带有空连接树的查询。 */ ResultPath * create_result_path(PlannerInfo *root, RelOptInfo *rel, PathTarget *target, List *resconstantqual) { ResultPath *pathnode =makeNode(ResultPath);//结果pathnode->path.pathtype = T_Result;//扫描路径类型pathnode ->path.parent = rel;//路径pathnode的Partentbuild_empty_join_rel->path.pathtarget = target;//目标列pathnode ->path.param_info = NULL; /* 没有其他关系... */ pathnode->path.parallel_aware = false; pathnode->path.parallel_safe = rel->consider_parallel;路径节点->path.parallel_workers = 0; //并行工作者的数量,设置为0 pathnode->path.pathkeys = NIL;// pathnode->quals = resconstantqual;//表达式 /* 几乎不值得定义一个 cost_result() 函数...就这样做 */ pathnode ->path.rows = 1;//行数为1 pathnode->path.startup_成本=目标->成本.启动; pathnode->path.total_cost = target->cost.startup + cpu_tuple_cost + target->cost.per_tuple; /* * 添加 qual 成本(如果有的话) --- 但我们忽略它的选择性,因为无论 qual 是什么,我们的 * rowcount 估计值都应该是 1。 */ if ( resconstantqual) { QualCost Qual_cost; Cost_qual_Eval (& Qual_cost, Resconstantqual, Root);/*Resconstantqual 在启动时评估*/Pathnode->Path. Startup_COST += Qual_cost.Startup+Qual_cost.per_tuple; PATHNODE-> PATH.TOTAL_COST+= QUAL_COST.STARTUP + qual_cost.per_tuple; } 返回路径节点; }

build_empty_join_rel

/* * build_empty_join_rel * 构建一个虚拟连接关系,描述一组空的基本rel。 * * 这用于带有空 FROM 子句的查询,例如“SELECT 2+2”或 *“INSERT INTO foo VALUES(...)”。我们不会非常努力地使空 * joinrel 完全有效,因为不会用它进行真正的规划 --- * 我们只需要它携带一个简单的结果路径或query_planner() 的 t。 */ RelOptInfo * build_empty_join_rel(PlannerInfo *root) { RelOptInfo *joinrel; /* 虚拟连接关系应该是唯一的... */ Assert(root->join_rel_list == NIL); joinrel = makeNode(RelOptInfo); joinrel->reloptkind = RELOPT_JOINREL; joinrel->relids = NULL; /* 空集 */ joinrel->rows = 1; /* 我们为这种情况生成一行 */ Joinrel->rtekind = RTE_JOIN; joinrel->reltarget = create_empty_pathtarget(); root->join_rel_list = lappend(root->join_rel_list, joinrel);返回连接; }

3.跟踪分析

(gdb) b query_plannerBreakpoint 1 at 0x76942c: file planmain.c, line 57.(gdb) cContinuing.Breakpoint 1, query_planner (root=0x275c878, tlist=0x277fd78, qp_callback=0x76e97d  , qp_extra=0x7ffdd435d490) at planmain.c:5757 查询 *parse = root->parse;(gdb) n67 if (parse->jointree->fromlist == NIL)( gdb) 70 Final_rel = build_empty_join_rel(root);(gdb ) 78 if (root->glob->parallelModeOK)# 创建了空 RELOPT_JOINREL(gdb)p * Final_rel $ 4 = {类型= T_RelOptInfo,reloptkind = RELOPT_JOINR EL,relids = 0x0,行= 1,考虑_启动= false,考虑_param_startup = false,考虑_并行= false,reltarget = 0x277fda8,路径列表= 0x0,ppilist = 0x0,部分路径列表= 0x0,最便宜的启动路径= 0x0,最便宜的总路径= 0x0,最便宜的唯一路径= 0x0,最便宜的参数化路径= 0x0,直接横向_relids = 0x0,横向_relids = 0x0,relid = 0,reltablespace = 0,rtekind = RTE_JOIN,min_attr = 0,max_attr = 0、需要属性 = 0x0、attr_widths = 0x0、lateral_vars = 0x0、lateral_referencers = 0x0、indexlist = 0x0、statlist = 0x0、页数 = 0、元组 = 0、allvisfrac = 0、subroot = 0x0、subplan_params = 0x0、rel_para llel_workers = 0、serverid = 0 ,userid = 0,useridiscurrent = false,fdwroutine = 0x0,fdw_private = 0x0,unique_for_rels = 0x0,non_unique_for_rels = 0x0,baserestrictinfo = 0x0,baserestrictcost = {startup = 0,per_tuple = 0},baserestrict_min_security = 0,joininfo = 0x0,有_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}...(gdb)stepadd_path(parent_rel = 0x275cc88,new_path = 0x 275c498)在pathnode.c:424424布尔accept_new = true; /* 除非我们找到更好的旧路径 */#Created path(ResultPath)(gdb) p *new_path$6 = {type = T_ResultPath, pathtype = T_Result, Parent = 0x275cc88, pathtarget = 0x277fda8, param_info = 0x0, parallel_aware = false, parallel_safe = true,parallel_workers = 0,rows = 1,startup_cost = 0,total_cost = 0.01,pathkeys = 0x0}(gdb) finishRun 直到从 #0 add_path (parent_rel=0x275cc88, new_path=0x275c498) at pathnode.c:425query_planner 退出( root=0x275c878, tlist =0x277fd78, qp_callback=0x76e97d , qp_extra=0x7ffdd435d490) at planmain.c:8989 set_cheapest(final_rel);...98 return Final_rel;(gdb) 267 }#返回值 (gdb) p * Final_rel$8 = {类型 = T_RelOptInfo, reloptkind = RELOPT_JOINREL,relids = 0x0,rows = 1,consider_startup = false,consider_param_startup = false,consider_parallel = true,reltarget = 0x277fda8,pathlist = 0x277fe68,ppilist = 0x0,partial_pathlist = 0x0,最便宜的_startup_path = 0x275c498,最便宜的_total_path = 0x275c498,最便宜的唯一路径= 0x0,最便宜的_参数化_路径 = 0x2 77feb8,直接_横向_relids = 0x0,横向_relids = 0x0,relid = 0,reltablespace = 0,rtekind = RTE_JOIN,min_attr = 0,最大_attr = 0,attr_needed = 0x0,attr_widths = 0x0,横向_vars = 0x0,横向参考 = 0x0 、indexlist = 0x0、statlist = 0x0、页数 = 0、元组 = 0、allvisfrac = 0、subroot = 0x0、subplan_params = 0x0、rel_parallel_workers = 0、serverid = 0、userid = 0、useridiscurrent = false、fdwroutine = 0x0、fdw_private = 0x0、unique_for_rels = 0x0、non_unique_for_rels = 0x0、baserestrictinfo = 0x0、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}(gdb) p *final_rel->cheapest_total_path$9 = {type = T_ResultP ath,pathtype = T_Result,父级 = 0x275cc88,pathtarget = 0x277fda8,param_info = 0x0,parallel_aware = false,parallel_safe = true,parallel_workers = 0,行数 = 1,startup_cost = 0,total_cost = 0.01,pathkeys = 0x0}(gd b ) #完成!

关于“PostgreSQL中query_planner函数处理的逻辑分析”的研究到此结束。希望能够解答大家的疑惑。理论与实践相结合,能够更好的帮助大家学习,去尝试吧!如果您想继续了解更多相关知识,请继续关注网站。小编会继续努力,给大家带来更多实用的文章!

1. 本站所有资源来源于用户上传或网络,仅作为参考研究使用,如有侵权请邮件联系站长!
2. 本站积分货币获取途径以及用途的解读,想在本站混的好,请务必认真阅读!
3. 本站强烈打击盗版/破解等有损他人权益和违法作为,请各位会员支持正版!
4. 编程技术 > PostgreSQL中query_planner函数的处理逻辑分析

用户评论