您好,登錄后才能下訂單哦!
這篇文章主要介紹“PostgreSQL中query_planner函數的處理邏輯分析”,在日常操作中,相信很多人在PostgreSQL中query_planner函數的處理邏輯分析問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”PostgreSQL中query_planner函數的處理邏輯分析”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
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; /* * Is the given relation a simple relation i.e a base or "other" member * relation? */ #define IS_SIMPLE_REL(rel) \ ((rel)->reloptkind == RELOPT_BASEREL || \ (rel)->reloptkind == RELOPT_OTHER_MEMBER_REL) /* Is the given relation a join relation? */ #define IS_JOIN_REL(rel) \ ((rel)->reloptkind == RELOPT_JOINREL || \ (rel)->reloptkind == RELOPT_OTHER_JOINREL) /* Is the given relation an upper relation? */ #define IS_UPPER_REL(rel) \ ((rel)->reloptkind == RELOPT_UPPER_REL || \ (rel)->reloptkind == RELOPT_OTHER_UPPER_REL) /* Is the given relation an "other" relation? */ #define IS_OTHER_REL(rel) \ ((rel)->reloptkind == RELOPT_OTHER_MEMBER_REL || \ (rel)->reloptkind == RELOPT_OTHER_JOINREL || \ (rel)->reloptkind == RELOPT_OTHER_UPPER_REL) typedef struct RelOptInfo { NodeTag type;//節點標識 RelOptKind reloptkind;//RelOpt類型 /* all relations included in this RelOptInfo */ Relids relids; /*Relids(rtindex)集合 set of base relids (rangetable indexes) */ /* size estimates generated by planner */ double rows; /*結果元組的估算數量 estimated number of result tuples */ /* per-relation planner control flags */ bool consider_startup; /*是否考慮啟動成本?是,需要保留啟動成本低的路徑 keep cheap-startup-cost paths? */ bool consider_param_startup; /*是否考慮參數化?的路徑 ditto, for parameterized paths? */ bool consider_parallel; /*是否考慮并行處理路徑 consider parallel paths? */ /* default result targetlist for Paths scanning this relation */ struct PathTarget *reltarget; /*掃描該Relation時默認的結果 list of Vars/Exprs, cost, width */ /* materialization information */ List *pathlist; /*訪問路徑鏈表 Path structures */ List *ppilist; /*路徑鏈表中使用參數化路徑進行 ParamPathInfos used in pathlist */ List *partial_pathlist; /* partial Paths */ struct Path *cheapest_startup_path;//代價最低的啟動路徑 struct Path *cheapest_total_path;//代價最低的整體路徑 struct Path *cheapest_unique_path;//代價最低的獲取唯一值的路徑 List *cheapest_parameterized_paths;//代價最低的參數化?路徑鏈表 /* parameterization information needed for both base rels and join rels */ /* (see also lateral_vars and lateral_referencers) */ Relids direct_lateral_relids; /*使用lateral語法,需依賴的Relids rels directly laterally referenced */ Relids lateral_relids; /* minimum parameterization of rel */ /* information about a base rel (not set for join rels!) */ //reloptkind=RELOPT_BASEREL時使用的數據結構 Index relid; /* Relation ID */ Oid reltablespace; /* 表空間 containing tablespace */ RTEKind rtekind; /* 基表?子查詢?還是函數等等?RELATION, SUBQUERY, FUNCTION, etc */ AttrNumber min_attr; /* 最小的屬性編號 smallest attrno of rel (often <0) */ AttrNumber max_attr; /* 最大的屬性編號 largest attrno of rel */ Relids *attr_needed; /* 數組 array indexed [min_attr .. max_attr] */ int32 *attr_widths; /* 屬性寬度 array indexed [min_attr .. max_attr] */ List *lateral_vars; /* 關系依賴的Vars/PHVs LATERAL Vars and PHVs referenced by rel */ Relids lateral_referencers; /*依賴該關系的Relids rels that reference me laterally */ List *indexlist; /* 該關系的IndexOptInfo鏈表 list of IndexOptInfo */ List *statlist; /* 統計信息鏈表 list of StatisticExtInfo */ BlockNumber pages; /* 塊數 size estimates derived from pg_class */ double tuples; /* 元組數 */ double allvisfrac; /* ? */ PlannerInfo *subroot; /* 如為子查詢,存儲子查詢的root if subquery */ List *subplan_params; /* 如為子查詢,存儲子查詢的參數 if subquery */ int rel_parallel_workers; /* 并行執行,需要多少個workers? wanted number of parallel workers */ /* Information about foreign tables and foreign joins */ //FWD相關信息 Oid serverid; /* identifies server for the table or join */ Oid userid; /* identifies user to check access as */ bool useridiscurrent; /* join is only valid for current user */ /* use "struct FdwRoutine" to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; void *fdw_private; /* cache space for remembering if we have proven this relation unique */ //已知的,可保證唯一的Relids鏈表 List *unique_for_rels; /* known unique for these other relid * set(s) */ List *non_unique_for_rels; /* 已知的,不唯一的Relids鏈表 known not unique for these set(s) */ /* used by various scans and joins: */ List *baserestrictinfo; /* 如為基本關系,存儲約束條件 RestrictInfo structures (if base rel) */ QualCost baserestrictcost; /* 解析約束表達式的成本? cost of evaluating the above */ Index baserestrict_min_security; /* 最低安全等級 min security_level found in * baserestrictinfo */ List *joininfo; /* 連接語句的約束條件信息 RestrictInfo structures for join clauses * involving this rel */ bool has_eclass_joins; /* 是否存在等價類連接? T means joininfo is incomplete */ /* used by partitionwise joins: */ bool consider_partitionwise_join; /* 分區? consider partitionwise * join paths? (if * partitioned rel) */ Relids top_parent_relids; /* Relids of topmost parents (if "other" * rel) */ /* used for partitioned relations */ //分區表使用 PartitionScheme part_scheme; /* 分區的schema Partitioning scheme. */ int nparts; /* 分區數 number of partitions */ struct PartitionBoundInfoData *boundinfo; /* 分區邊界信息 Partition bounds */ List *partition_qual; /* 分區約束 partition constraint */ struct RelOptInfo **part_rels; /* 分區的RelOptInfo數組 Array of RelOptInfos of partitions, * stored in the same order of bounds */ List **partexprs; /* 非空分區鍵表達式 Non-nullable partition key expressions. */ List **nullable_partexprs; /* 可為空的分區鍵表達式 Nullable partition key expressions. */ List *partitioned_child_rels; /* RT Indexes鏈表 List of RT indexes. */ } RelOptInfo;
PathCostComparison
typedef enum { COSTS_EQUAL, /* 近似相等 path costs are fuzzily equal */ COSTS_BETTER1, /* 第一個路徑成本較低 first path is cheaper than second */ COSTS_BETTER2, /* 第二個相對較低 second path is cheaper than first */ COSTS_DIFFERENT /* 不管是哪個路徑,成本上都不占優勢 neither path dominates the other on cost */ } PathCostComparison;
ResultPath
/* * ResultPath represents use of a Result plan node to compute a variable-free * targetlist with no underlying tables (a "SELECT expressions" query). * The query could have a WHERE clause, too, represented by "quals". * * Note that quals is a list of bare clauses, not RestrictInfos. */ typedef struct ResultPath //表示無基礎表的結果計劃節點 { Path path;//掃描路徑 List *quals;//where語句表達式,bare clauses, not RestrictInfos } ResultPath;
/* * query_planner * Generate a path (that is, a simplified plan) for a basic query, * which may involve joins but not any fancier features. * * 為一個基本的查詢(可能涉及連接)生成訪問路徑(也可以視為一個簡化的計劃). * * Since query_planner does not handle the toplevel processing (grouping, * sorting, etc) it cannot select the best path by itself. Instead, it * returns the RelOptInfo for the top level of joining, and the caller * (grouping_planner) can choose among the surviving paths for the rel. * * query_planner不會處理頂層的處理過程(如最后的分組/排序等操作),因此,不能選擇最優的訪問路徑 * 該函數會返回RelOptInfo給最高層的連接,grouping_planner可以在剩下的路徑中進行選擇 * * root describes the query to plan * tlist is the target list the query should produce * (this is NOT necessarily root->parse->targetList!) * qp_callback is a function to compute query_pathkeys once it's safe to do so * qp_extra is optional extra data to pass to qp_callback * * root是計劃信息/tlist是投影列 * qp_callback是計算query_pathkeys的函數/qp_extra是傳遞給qp_callback的函數 * * Note: the PlannerInfo node also includes a query_pathkeys field, which * tells query_planner the sort order that is desired in the final output * plan. This value is *not* available at call time, but is computed by * qp_callback once we have completed merging the query's equivalence classes. * (We cannot construct canonical pathkeys until that's done.) */ RelOptInfo * query_planner(PlannerInfo *root, List *tlist, query_pathkeys_callback qp_callback, void *qp_extra) { Query *parse = root->parse;//查詢樹 List *joinlist; RelOptInfo *final_rel;//結果 Index rti;//RTE的index double total_pages;//總pages數 /* * If the query has an empty join tree, then it's something easy like * "SELECT 2+2;" or "INSERT ... VALUES()". Fall through quickly. */ if (parse->jointree->fromlist == NIL)//簡單SQL,無FROM/WHERE語句 { /* We need a dummy joinrel to describe the empty set of baserels */ final_rel = build_empty_join_rel(root);//創建返回結果 /* * If query allows parallelism in general, check whether the quals are * parallel-restricted. (We need not check final_rel->reltarget * because it's empty at this point. Anything parallel-restricted in * the query tlist will be dealt with later.) */ if (root->glob->parallelModeOK)//并行模式? final_rel->consider_parallel = is_parallel_safe(root, parse->jointree->quals); /* The only path for it is a trivial Result path */ add_path(final_rel, (Path *) create_result_path(root, final_rel, final_rel->reltarget, (List *) parse->jointree->quals));//添加訪問路徑 /* Select cheapest path (pretty easy in this case...) */ set_cheapest(final_rel);//選擇最優的訪問路徑 /* * We still are required to call qp_callback, in case it's something * like "SELECT 2+2 ORDER BY 1". */ root->canon_pathkeys = NIL; (*qp_callback) (root, qp_extra);//回調函數 return final_rel;//返回 } //其他代碼 ... }
add_path/set_cheapest
后續再行介紹
create_result_path
/* * create_result_path * Creates a path representing a Result-and-nothing-else plan. * * This is only used for degenerate cases, such as a query with an empty * jointree. */ 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;//路徑的partentbuild_empty_join_rel pathnode->path.pathtarget = target;//目標列 pathnode->path.param_info = NULL; /* there are no other rels... */ pathnode->path.parallel_aware = false; pathnode->path.parallel_safe = rel->consider_parallel; pathnode->path.parallel_workers = 0;//并行workers數目,設置為0 pathnode->path.pathkeys = NIL;// pathnode->quals = resconstantqual;//表達式 /* Hardly worth defining a cost_result() function ... just do it */ pathnode->path.rows = 1;//行數為1 pathnode->path.startup_cost = target->cost.startup; pathnode->path.total_cost = target->cost.startup + cpu_tuple_cost + target->cost.per_tuple; /* * Add cost of qual, if any --- but we ignore its selectivity, since our * rowcount estimate should be 1 no matter what the qual is. */ if (resconstantqual) { QualCost qual_cost; cost_qual_eval(&qual_cost, resconstantqual, root); /* resconstantqual is evaluated once at startup */ pathnode->path.startup_cost += qual_cost.startup + qual_cost.per_tuple; pathnode->path.total_cost += qual_cost.startup + qual_cost.per_tuple; } return pathnode; }
build_empty_join_rel
/* * build_empty_join_rel * Build a dummy join relation describing an empty set of base rels. * * This is used for queries with empty FROM clauses, such as "SELECT 2+2" or * "INSERT INTO foo VALUES(...)". We don't try very hard to make the empty * joinrel completely valid, since no real planning will be done with it --- * we just need it to carry a simple Result path out of query_planner(). */ RelOptInfo * build_empty_join_rel(PlannerInfo *root) { RelOptInfo *joinrel; /* The dummy join relation should be the only one ... */ Assert(root->join_rel_list == NIL); joinrel = makeNode(RelOptInfo); joinrel->reloptkind = RELOPT_JOINREL; joinrel->relids = NULL; /* empty set */ joinrel->rows = 1; /* we produce one row for such cases */ joinrel->rtekind = RTE_JOIN; joinrel->reltarget = create_empty_pathtarget(); root->join_rel_list = lappend(root->join_rel_list, joinrel); return joinrel; }
(gdb) b query_planner Breakpoint 1 at 0x76942c: file planmain.c, line 57. (gdb) c Continuing. Breakpoint 1, query_planner (root=0x275c878, tlist=0x277fd78, qp_callback=0x76e97d <standard_qp_callback>, qp_extra=0x7ffdd435d490) at planmain.c:57 57 Query *parse = root->parse; (gdb) n 67 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 = {type = T_RelOptInfo, reloptkind = RELOPT_JOINREL, relids = 0x0, rows = 1, consider_startup = false, consider_param_startup = false, consider_parallel = false, reltarget = 0x277fda8, 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 = 0, reltablespace = 0, rtekind = RTE_JOIN, min_attr = 0, max_attr = 0, attr_needed = 0x0, attr_widths = 0x0, lateral_vars = 0x0, lateral_referencers = 0x0, indexlist = 0x0, statlist = 0x0, pages = 0, tuples = 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) step add_path (parent_rel=0x275cc88, new_path=0x275c498) at pathnode.c:424 424 bool accept_new = true; /* unless we find a superior old path */ #創建的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) finish Run till exit from #0 add_path (parent_rel=0x275cc88, new_path=0x275c498) at pathnode.c:425 query_planner (root=0x275c878, tlist=0x277fd78, qp_callback=0x76e97d <standard_qp_callback>, qp_extra=0x7ffdd435d490) at planmain.c:89 89 set_cheapest(final_rel); ... 98 return final_rel; (gdb) 267 } #返回值 (gdb) p *final_rel $8 = {type = 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, cheapest_startup_path = 0x275c498, cheapest_total_path = 0x275c498, cheapest_unique_path = 0x0, cheapest_parameterized_paths = 0x277feb8, direct_lateral_relids = 0x0, lateral_relids = 0x0, relid = 0, reltablespace = 0, rtekind = RTE_JOIN, min_attr = 0, max_attr = 0, attr_needed = 0x0, attr_widths = 0x0, lateral_vars = 0x0, lateral_referencers = 0x0, indexlist = 0x0, statlist = 0x0, pages = 0, tuples = 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_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) #DONE!
到此,關于“PostgreSQL中query_planner函數的處理邏輯分析”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。