您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關(guān)PostgreSQL如何為append relation構(gòu)建訪問路徑的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
AppendRelInfo
當(dāng)我們將可繼承表(分區(qū)表)或UNION-ALL子查詢展開為“追加關(guān)系”(本質(zhì)上是子RTE的鏈表)時,為每個子RTE構(gòu)建一個AppendRelInfo。
/* * Append-relation info. * Append-relation信息. * * When we expand an inheritable table or a UNION-ALL subselect into an * "append relation" (essentially, a list of child RTEs), we build an * AppendRelInfo for each child RTE. The list of AppendRelInfos indicates * which child RTEs must be included when expanding the parent, and each node * carries information needed to translate Vars referencing the parent into * Vars referencing that child. * 當(dāng)我們將可繼承表(分區(qū)表)或UNION-ALL子查詢展開為“追加關(guān)系”(本質(zhì)上是子RTE的鏈表)時, * 為每個子RTE構(gòu)建一個AppendRelInfo。 * AppendRelInfos鏈表指示在展開父節(jié)點時必須包含哪些子rte, * 每個節(jié)點具有將引用父節(jié)點的Vars轉(zhuǎn)換為引用該子節(jié)點的Vars所需的所有信息。 * * These structs are kept in the PlannerInfo node's append_rel_list. * Note that we just throw all the structs into one list, and scan the * whole list when desiring to expand any one parent. We could have used * a more complex data structure (eg, one list per parent), but this would * be harder to update during operations such as pulling up subqueries, * and not really any easier to scan. Considering that typical queries * will not have many different append parents, it doesn't seem worthwhile * to complicate things. * 這些結(jié)構(gòu)體保存在PlannerInfo節(jié)點的append_rel_list中。 * 注意,只是將所有的結(jié)構(gòu)體放入一個鏈表中,并在希望展開任何父類時掃描整個鏈表。 * 本可以使用更復(fù)雜的數(shù)據(jù)結(jié)構(gòu)(例如,每個父節(jié)點一個列表), * 但是在提取子查詢之類的操作中更新它會更困難, * 而且實際上也不會更容易掃描。 * 考慮到典型的查詢不會有很多不同的附加項,因此似乎不值得將事情復(fù)雜化。 * * Note: after completion of the planner prep phase, any given RTE is an * append parent having entries in append_rel_list if and only if its * "inh" flag is set. We clear "inh" for plain tables that turn out not * to have inheritance children, and (in an abuse of the original meaning * of the flag) we set "inh" for subquery RTEs that turn out to be * flattenable UNION ALL queries. This lets us avoid useless searches * of append_rel_list. * 注意:計劃準(zhǔn)備階段完成后, * 當(dāng)且僅當(dāng)它的“inh”標(biāo)志已設(shè)置時,給定的RTE是一個append parent在append_rel_list中的一個條目。 * 我們?yōu)闆]有child的平面表清除“inh”標(biāo)記, * 同時(有濫用標(biāo)記的嫌疑)為UNION ALL查詢中的子查詢RTEs設(shè)置“inh”標(biāo)記。 * 這樣可以避免對append_rel_list進行無用的搜索。 * * Note: the data structure assumes that append-rel members are single * baserels. This is OK for inheritance, but it prevents us from pulling * up a UNION ALL member subquery if it contains a join. While that could * be fixed with a more complex data structure, at present there's not much * point because no improvement in the plan could result. * 注意:數(shù)據(jù)結(jié)構(gòu)假定附加的rel成員是獨立的baserels。 * 這對于繼承來說是可以的,但是如果UNION ALL member子查詢包含一個join, * 那么它將阻止我們提取UNION ALL member子查詢。 * 雖然可以用更復(fù)雜的數(shù)據(jù)結(jié)構(gòu)解決這個問題,但目前沒有太大意義,因為該計劃可能不會有任何改進。 */ typedef struct AppendRelInfo { NodeTag type; /* * These fields uniquely identify this append relationship. There can be * (in fact, always should be) multiple AppendRelInfos for the same * parent_relid, but never more than one per child_relid, since a given * RTE cannot be a child of more than one append parent. * 這些字段惟一地標(biāo)識這個append relationship。 * 對于同一個parent_relid可以有(實際上應(yīng)該總是)多個AppendRelInfos, * 但是每個child_relid不能有多個AppendRelInfos, * 因為給定的RTE不能是多個append parent的子節(jié)點。 */ Index parent_relid; /* parent rel的RT索引;RT index of append parent rel */ Index child_relid; /* child rel的RT索引;RT index of append child rel */ /* * For an inheritance appendrel, the parent and child are both regular * relations, and we store their rowtype OIDs here for use in translating * whole-row Vars. For a UNION-ALL appendrel, the parent and child are * both subqueries with no named rowtype, and we store InvalidOid here. * 對于繼承appendrel,父類和子類都是普通關(guān)系, * 我們將它們的rowtype OIDs存儲在這里,用于轉(zhuǎn)換whole-row Vars。 * 對于UNION-ALL appendrel,父查詢和子查詢都是沒有指定行類型的子查詢, * 我們在這里存儲InvalidOid。 */ Oid parent_reltype; /* OID of parent's composite type */ Oid child_reltype; /* OID of child's composite type */ /* * The N'th element of this list is a Var or expression representing the * child column corresponding to the N'th column of the parent. This is * used to translate Vars referencing the parent rel into references to * the child. A list element is NULL if it corresponds to a dropped * column of the parent (this is only possible for inheritance cases, not * UNION ALL). The list elements are always simple Vars for inheritance * cases, but can be arbitrary expressions in UNION ALL cases. * 這個列表的第N個元素是一個Var或表達式,表示與父元素的第N列對應(yīng)的子列。 * 這用于將引用parent rel的Vars轉(zhuǎn)換為對子rel的引用。 * 如果鏈表元素與父元素的已刪除列相對應(yīng),則該元素為NULL * (這只適用于繼承情況,而不是UNION ALL)。 * 對于繼承情況,鏈表元素總是簡單的變量,但是可以是UNION ALL情況下的任意表達式。 * * Notice we only store entries for user columns (attno > 0). Whole-row * Vars are special-cased, and system columns (attno < 0) need no special * translation since their attnos are the same for all tables. * 注意,我們只存儲用戶列的條目(attno > 0)。 * Whole-row Vars是大小寫敏感的,系統(tǒng)列(attno < 0)不需要特別的轉(zhuǎn)換, * 因為它們的attno對所有表都是相同的。 * * Caution: the Vars have varlevelsup = 0. Be careful to adjust as needed * when copying into a subquery. * 注意:Vars的varlevelsup = 0。 * 在將數(shù)據(jù)復(fù)制到子查詢時,要注意根據(jù)需要進行調(diào)整。 */ //child's Vars中的表達式 List *translated_vars; /* Expressions in the child's Vars */ /* * We store the parent table's OID here for inheritance, or InvalidOid for * UNION ALL. This is only needed to help in generating error messages if * an attempt is made to reference a dropped parent column. * 我們將父表的OID存儲在這里用于繼承, * 如為UNION ALL,則這里存儲的是InvalidOid。 * 只有在試圖引用已刪除的父列時,才需要這樣做來幫助生成錯誤消息。 */ Oid parent_reloid; /* OID of parent relation */ } AppendRelInfo;
RelOptInfo
規(guī)劃器/優(yōu)化器使用的關(guān)系信息結(jié)構(gòu)體
參見PostgreSQL 源碼解讀(99)- 分區(qū)表#5(數(shù)據(jù)查詢路由#2-RelOptInfo數(shù)據(jù)結(jié)構(gòu))
set_rel_pathlist函數(shù)為基礎(chǔ)關(guān)系構(gòu)建訪問路徑.
//是否DUMMY訪問路徑 #define IS_DUMMY_PATH(p) \ (IsA((p), AppendPath) && ((AppendPath *) (p))->subpaths == NIL) /* A relation that's been proven empty will have one path that is dummy */ //已被證明為空的關(guān)系會含有一個虛擬dummy的訪問路徑 #define IS_DUMMY_REL(r) \ ((r)->cheapest_total_path != NULL && \ IS_DUMMY_PATH((r)->cheapest_total_path)) /* * set_rel_pathlist * Build access paths for a base relation * 為基礎(chǔ)關(guān)系構(gòu)建訪問路徑 */ static void set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte) { if (IS_DUMMY_REL(rel)) { /* We already proved the relation empty, so nothing more to do */ //不需要做任何處理 } else if (rte->inh) { /* It's an "append relation", process accordingly */ //append relation,調(diào)用set_append_rel_pathlist處理 set_append_rel_pathlist(root, rel, rti, rte); } else {//其他類型的關(guān)系 switch (rel->rtekind) { case RTE_RELATION://基礎(chǔ)關(guān)系 if (rte->relkind == RELKIND_FOREIGN_TABLE) { /* Foreign table */ //外部表 set_foreign_pathlist(root, rel, rte); } else if (rte->tablesample != NULL) { /* Sampled relation */ //數(shù)據(jù)表采樣 set_tablesample_rel_pathlist(root, rel, rte); } else { /* Plain relation */ //常規(guī)關(guān)系 set_plain_rel_pathlist(root, rel, rte); } break; case RTE_SUBQUERY: /* Subquery --- fully handled during set_rel_size */ //子查詢 break; case RTE_FUNCTION: /* RangeFunction */ set_function_pathlist(root, rel, rte); break; case RTE_TABLEFUNC: /* Table Function */ set_tablefunc_pathlist(root, rel, rte); break; case RTE_VALUES: /* Values list */ set_values_pathlist(root, rel, rte); break; case RTE_CTE: /* CTE reference --- fully handled during set_rel_size */ break; case RTE_NAMEDTUPLESTORE: /* tuplestore reference --- fully handled during set_rel_size */ break; default: elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind); break; } } /* * If this is a baserel, we should normally consider gathering any partial * paths we may have created for it. * 如為基礎(chǔ)關(guān)系,通常來說需要考慮聚集gathering所有的部分路徑(先前已構(gòu)建) * * However, if this is an inheritance child, skip it. Otherwise, we could * end up with a very large number of gather nodes, each trying to grab * its own pool of workers. Instead, we'll consider gathering partial * paths for the parent appendrel. * 但是,如果這是一個繼承關(guān)系中的子表,忽略它. * 否則的話,我們最好可能會有很大量的Gather節(jié)點,每一個都嘗試grab它自己worker的輸出. * 相反我們的處理是為父append relation生成gathering部分訪問路徑. * * Also, if this is the topmost scan/join rel (that is, the only baserel), * we postpone this until the final scan/join targelist is available (see * grouping_planner). * 另外,如果這是最高層的scan/join關(guān)系(基礎(chǔ)關(guān)系), * 在完成了最后的scan/join投影列處理后才進行相應(yīng)的處理. */ if (rel->reloptkind == RELOPT_BASEREL && bms_membership(root->all_baserels) != BMS_SINGLETON) generate_gather_paths(root, rel, false); /* * Allow a plugin to editorialize on the set of Paths for this base * relation. It could add new paths (such as CustomPaths) by calling * add_path(), or delete or modify paths added by the core code. * 調(diào)用鉤子函數(shù). * 插件可以通過調(diào)用add_path添加新的訪問路徑(比如CustomPaths等), * 或者刪除/修改核心代碼生成的訪問路徑. */ if (set_rel_pathlist_hook) (*set_rel_pathlist_hook) (root, rel, rti, rte); /* Now find the cheapest of the paths for this rel */ //為rel找到成本最低的訪問路徑 set_cheapest(rel); #ifdef OPTIMIZER_DEBUG debug_print_rel(root, rel); #endif } /* * set_append_rel_pathlist * Build access paths for an "append relation" * 為append relation構(gòu)建訪問路徑 */ static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte) { int parentRTindex = rti; List *live_childrels = NIL; ListCell *l; /* * Generate access paths for each member relation, and remember the * non-dummy children. * 為每個成員關(guān)系生成訪問路徑,并"記住"非虛擬子節(jié)點。 */ foreach(l, root->append_rel_list)//遍歷鏈表 { AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);//獲取AppendRelInfo int childRTindex; RangeTblEntry *childRTE; RelOptInfo *childrel; /* append_rel_list contains all append rels; ignore others */ //append_rel_list含有所有的append relations,忽略其他的rels if (appinfo->parent_relid != parentRTindex) continue; /* Re-locate the child RTE and RelOptInfo */ //重新定位子RTE和RelOptInfo childRTindex = appinfo->child_relid; childRTE = root->simple_rte_array[childRTindex]; childrel = root->simple_rel_array[childRTindex]; /* * If set_append_rel_size() decided the parent appendrel was * parallel-unsafe at some point after visiting this child rel, we * need to propagate the unsafety marking down to the child, so that * we don't generate useless partial paths for it. * 如果set_append_rel_size()函數(shù)在訪問了子關(guān)系后, * 在某些點上斷定父append relation是非并行安全的, * 我們需要分發(fā)不安全的標(biāo)記到子關(guān)系中,以免產(chǎn)生無用的部分訪問路徑. */ if (!rel->consider_parallel) childrel->consider_parallel = false; /* * Compute the child's access paths. * "計算"子關(guān)系的訪問路徑 */ set_rel_pathlist(root, childrel, childRTindex, childRTE); /* * If child is dummy, ignore it. * 如為虛擬關(guān)系,忽略之 */ if (IS_DUMMY_REL(childrel)) continue; /* Bubble up childrel's partitioned children. */ // if (rel->part_scheme) rel->partitioned_child_rels = list_concat(rel->partitioned_child_rels, list_copy(childrel->partitioned_child_rels)); /* * Child is live, so add it to the live_childrels list for use below. * 添加到live_childrels鏈表中 */ live_childrels = lappend(live_childrels, childrel); } /* Add paths to the append relation. */ //添加訪問路徑到append relation中 add_paths_to_append_rel(root, rel, live_childrels); } /* * add_paths_to_append_rel * Generate paths for the given append relation given the set of non-dummy * child rels. * 基于非虛擬子關(guān)系集合為給定的append relation生成訪問路徑 * * The function collects all parameterizations and orderings supported by the * non-dummy children. For every such parameterization or ordering, it creates * an append path collecting one path from each non-dummy child with given * parameterization or ordering. Similarly it collects partial paths from * non-dummy children to create partial append paths. * 該函數(shù)收集所有的非虛擬關(guān)系支持的參數(shù)化和排序訪問路徑. * 對于每一個參數(shù)化或排序的訪問路徑,它創(chuàng)建一個附加路徑, * 從每個具有給定參數(shù)化或排序的非虛擬子節(jié)點收集相關(guān)信息。 * 類似的,從非虛擬子關(guān)系中收集部分訪問路徑用以創(chuàng)建部分append路徑. */ void add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, List *live_childrels) { List *subpaths = NIL; bool subpaths_valid = true; List *partial_subpaths = NIL; List *pa_partial_subpaths = NIL; List *pa_nonpartial_subpaths = NIL; bool partial_subpaths_valid = true; bool pa_subpaths_valid; List *all_child_pathkeys = NIL; List *all_child_outers = NIL; ListCell *l; List *partitioned_rels = NIL; double partial_rows = -1; /* If appropriate, consider parallel append */ //如可以,考慮并行append pa_subpaths_valid = enable_parallel_append && rel->consider_parallel; /* * AppendPath generated for partitioned tables must record the RT indexes * of partitioned tables that are direct or indirect children of this * Append rel. * 為分區(qū)表生成的AppendPath必須記錄屬于該分區(qū)表(Append Rel)的直接或間接子關(guān)系的RT索引 * * AppendPath may be for a sub-query RTE (UNION ALL), in which case, 'rel' * itself does not represent a partitioned relation, but the child sub- * queries may contain references to partitioned relations. The loop * below will look for such children and collect them in a list to be * passed to the path creation function. (This assumes that we don't need * to look through multiple levels of subquery RTEs; if we ever do, we * could consider stuffing the list we generate here into sub-query RTE's * RelOptInfo, just like we do for partitioned rels, which would be used * when populating our parent rel with paths. For the present, that * appears to be unnecessary.) * AppendPath可能是子查詢RTE(UNION ALL),在這種情況下,rel自身并不代表分區(qū)表, * 但child sub-queries可能含有分區(qū)表的依賴. * 以下的循環(huán)會尋找這樣的子關(guān)系,存儲在鏈表中,并作為參數(shù)傳給訪問路徑創(chuàng)建函數(shù). * (這是假設(shè)我們不需要遍歷多個層次的子查詢RTEs,如果我們曾經(jīng)這樣做了, * 這好比分區(qū)表的做法,可以考慮把生成的鏈表放到子查詢的RTE's RelOptInfo結(jié)構(gòu)體中, * 用于使用訪問路徑填充父關(guān)系.不過目前來說,這樣做是不需要的.) */ if (rel->part_scheme != NULL) { if (IS_SIMPLE_REL(rel)) partitioned_rels = list_make1(rel->partitioned_child_rels); else if (IS_JOIN_REL(rel)) { int relid = -1; List *partrels = NIL; /* * For a partitioned joinrel, concatenate the component rels' * partitioned_child_rels lists. * 對于分區(qū)連接rel,連接rels的partitioned_child_rels鏈表 * */ while ((relid = bms_next_member(rel->relids, relid)) >= 0) { RelOptInfo *component; Assert(relid >= 1 && relid < root->simple_rel_array_size); component = root->simple_rel_array[relid]; Assert(component->part_scheme != NULL); Assert(list_length(component->partitioned_child_rels) >= 1); partrels = list_concat(partrels, list_copy(component->partitioned_child_rels)); } partitioned_rels = list_make1(partrels); } Assert(list_length(partitioned_rels) >= 1); } /* * For every non-dummy child, remember the cheapest path. Also, identify * all pathkeys (orderings) and parameterizations (required_outer sets) * available for the non-dummy member relations. * 對于每一個非虛擬子關(guān)系,記錄成本最低的訪問路徑. * 同時,為每一個非虛擬成員關(guān)系標(biāo)識所有的路徑鍵(排序)和可用的參數(shù)化信息(required_outer集合) */ foreach(l, live_childrels)//遍歷 { RelOptInfo *childrel = lfirst(l); ListCell *lcp; Path *cheapest_partial_path = NULL; /* * For UNION ALLs with non-empty partitioned_child_rels, accumulate * the Lists of child relations. * 對于包含非空的partitioned_child_rels的UNION ALLs操作, * 累積子關(guān)系鏈表 */ if (rel->rtekind == RTE_SUBQUERY && childrel->partitioned_child_rels != NIL) partitioned_rels = lappend(partitioned_rels, childrel->partitioned_child_rels); /* * If child has an unparameterized cheapest-total path, add that to * the unparameterized Append path we are constructing for the parent. * If not, there's no workable unparameterized path. * 如果子關(guān)系存在非參數(shù)化的總成本最低的訪問路徑, * 添加此路徑到我們?yōu)楦戈P(guān)系構(gòu)建的非參數(shù)化的Append訪問路徑中. * * With partitionwise aggregates, the child rel's pathlist may be * empty, so don't assume that a path exists here. * 使用partitionwise聚合,子關(guān)系的訪問路徑鏈表可能是空的,不能假設(shè)其中存在訪問路徑 */ if (childrel->pathlist != NIL && childrel->cheapest_total_path->param_info == NULL) accumulate_append_subpath(childrel->cheapest_total_path, &subpaths, NULL); else subpaths_valid = false; /* Same idea, but for a partial plan. */ //同樣的思路,處理并行處理中的部分計劃 if (childrel->partial_pathlist != NIL) { cheapest_partial_path = linitial(childrel->partial_pathlist); accumulate_append_subpath(cheapest_partial_path, &partial_subpaths, NULL); } else partial_subpaths_valid = false; /* * Same idea, but for a parallel append mixing partial and non-partial * paths. * 同樣的,處理并行append混合并行/非并行訪問路徑 */ if (pa_subpaths_valid) { Path *nppath = NULL; nppath = get_cheapest_parallel_safe_total_inner(childrel->pathlist); if (cheapest_partial_path == NULL && nppath == NULL) { /* Neither a partial nor a parallel-safe path? Forget it. */ //不是部分路徑,也不是并行安全的路徑,跳過 pa_subpaths_valid = false; } else if (nppath == NULL || (cheapest_partial_path != NULL && cheapest_partial_path->total_cost < nppath->total_cost)) { /* Partial path is cheaper or the only option. */ //部分路徑成本更低或者是唯一的選項 Assert(cheapest_partial_path != NULL); accumulate_append_subpath(cheapest_partial_path, &pa_partial_subpaths, &pa_nonpartial_subpaths); } else { /* * Either we've got only a non-partial path, or we think that * a single backend can execute the best non-partial path * faster than all the parallel backends working together can * execute the best partial path. * 這時候,要么得到了一個非并行訪問路徑,或者我們認為一個單獨的后臺進程 * 執(zhí)行最好的非并行訪問路徑會比索引的并行進程一起執(zhí)行最好的部分路徑還要好. * * It might make sense to be more aggressive here. Even if * the best non-partial path is more expensive than the best * partial path, it could still be better to choose the * non-partial path if there are several such paths that can * be given to different workers. For now, we don't try to * figure that out. * 在這里,采取更積極的態(tài)度是有道理的. * 甚至最好的非部分路徑比最好的并行部分路徑成本更高,仍然需要選擇非并行路徑, * 如果多個這樣的路徑可能會分派到不同的worker上. * 現(xiàn)在不需要指出這一點. */ accumulate_append_subpath(nppath, &pa_nonpartial_subpaths, NULL); } } /* * Collect lists of all the available path orderings and * parameterizations for all the children. We use these as a * heuristic to indicate which sort orderings and parameterizations we * should build Append and MergeAppend paths for. * 收集子關(guān)系所有可用的排序和參數(shù)化路徑鏈表. * 我們采用啟發(fā)式的規(guī)則判斷Append和MergeAppend訪問路徑使用哪個排序和參數(shù)化信息 */ foreach(lcp, childrel->pathlist) { Path *childpath = (Path *) lfirst(lcp); List *childkeys = childpath->pathkeys; Relids childouter = PATH_REQ_OUTER(childpath); /* Unsorted paths don't contribute to pathkey list */ //未排序的訪問路徑,不需要分發(fā)到路徑鍵鏈表中 if (childkeys != NIL) { ListCell *lpk; bool found = false; /* Have we already seen this ordering? */ foreach(lpk, all_child_pathkeys) { List *existing_pathkeys = (List *) lfirst(lpk); if (compare_pathkeys(existing_pathkeys, childkeys) == PATHKEYS_EQUAL) { found = true; break; } } if (!found) { /* No, so add it to all_child_pathkeys */ all_child_pathkeys = lappend(all_child_pathkeys, childkeys); } } /* Unparameterized paths don't contribute to param-set list */ //非參數(shù)訪問路徑無需分發(fā)到參數(shù)化集合鏈表中 if (childouter) { ListCell *lco; bool found = false; /* Have we already seen this param set? */ foreach(lco, all_child_outers) { Relids existing_outers = (Relids) lfirst(lco); if (bms_equal(existing_outers, childouter)) { found = true; break; } } if (!found) { /* No, so add it to all_child_outers */ all_child_outers = lappend(all_child_outers, childouter); } } } } /* * If we found unparameterized paths for all children, build an unordered, * unparameterized Append path for the rel. (Note: this is correct even * if we have zero or one live subpath due to constraint exclusion.) * 如存在子關(guān)系的非參數(shù)化訪問路徑,構(gòu)建未排序/未參數(shù)化的Append訪問路徑. * (注意:如果存在約束排除,我們只剩下有0或1個存活的subpath,這樣的處理也說OK的) */ if (subpaths_valid) add_path(rel, (Path *) create_append_path(root, rel, subpaths, NIL, NULL, 0, false, partitioned_rels, -1)); /* * Consider an append of unordered, unparameterized partial paths. Make * it parallel-aware if possible. * 嘗試未排序/未參數(shù)化的部分Append訪問路徑. * 如可能,構(gòu)建parallel-aware訪問路徑. */ if (partial_subpaths_valid) { AppendPath *appendpath; ListCell *lc; int parallel_workers = 0; /* Find the highest number of workers requested for any subpath. */ //為子訪問路徑尋找最多數(shù)量的wokers foreach(lc, partial_subpaths) { Path *path = lfirst(lc); parallel_workers = Max(parallel_workers, path->parallel_workers); } Assert(parallel_workers > 0); /* * If the use of parallel append is permitted, always request at least * log2(# of children) workers. We assume it can be useful to have * extra workers in this case because they will be spread out across * the children. The precise formula is just a guess, but we don't * want to end up with a radically different answer for a table with N * partitions vs. an unpartitioned table with the same data, so the * use of some kind of log-scaling here seems to make some sense. * 如允許使用并行append,那么至少需要log2(子關(guān)系個數(shù))個workers. * 經(jīng)過擴展后,假定可以使用額外的workers. * 并不存在精確的計算公式,目前只是猜測而已,但是對于有相同數(shù)據(jù)的N個分區(qū)的分區(qū)表和非分區(qū)表來說, * 答案是不一樣的,因此在這里使用對數(shù)的計算方法是OK的. */ if (enable_parallel_append) { parallel_workers = Max(parallel_workers, fls(list_length(live_childrels)));//上限值 parallel_workers = Min(parallel_workers, max_parallel_workers_per_gather);//下限值 } Assert(parallel_workers > 0); /* Generate a partial append path. */ //生成并行部分append訪問路徑 appendpath = create_append_path(root, rel, NIL, partial_subpaths, NULL, parallel_workers, enable_parallel_append, partitioned_rels, -1); /* * Make sure any subsequent partial paths use the same row count * estimate. * 確保所有的子部分路徑使用相同的行數(shù)估算 */ partial_rows = appendpath->path.rows; /* Add the path. */ //添加路徑 add_partial_path(rel, (Path *) appendpath); } /* * Consider a parallel-aware append using a mix of partial and non-partial * paths. (This only makes sense if there's at least one child which has * a non-partial path that is substantially cheaper than any partial path; * otherwise, we should use the append path added in the previous step.) * 使用混合的部分和非部分并行的append. */ if (pa_subpaths_valid && pa_nonpartial_subpaths != NIL) { AppendPath *appendpath; ListCell *lc; int parallel_workers = 0; /* * Find the highest number of workers requested for any partial * subpath. */ foreach(lc, pa_partial_subpaths) { Path *path = lfirst(lc); parallel_workers = Max(parallel_workers, path->parallel_workers); } /* * Same formula here as above. It's even more important in this * instance because the non-partial paths won't contribute anything to * the planned number of parallel workers. */ parallel_workers = Max(parallel_workers, fls(list_length(live_childrels))); parallel_workers = Min(parallel_workers, max_parallel_workers_per_gather); Assert(parallel_workers > 0); appendpath = create_append_path(root, rel, pa_nonpartial_subpaths, pa_partial_subpaths, NULL, parallel_workers, true, partitioned_rels, partial_rows); add_partial_path(rel, (Path *) appendpath); } /* * Also build unparameterized MergeAppend paths based on the collected * list of child pathkeys. * 基于收集的子路徑鍵,構(gòu)建非參數(shù)化的MergeAppend訪問路徑 */ if (subpaths_valid) generate_mergeappend_paths(root, rel, live_childrels, all_child_pathkeys, partitioned_rels); /* * Build Append paths for each parameterization seen among the child rels. * (This may look pretty expensive, but in most cases of practical * interest, the child rels will expose mostly the same parameterizations, * so that not that many cases actually get considered here.) * 為每一個參數(shù)化的子關(guān)系構(gòu)建Append訪問路徑. * (看起來成本客觀,但在大多數(shù)情況下,子關(guān)系使用同樣的參數(shù)化信息,因此實際上并不是經(jīng)常發(fā)現(xiàn)) * * The Append node itself cannot enforce quals, so all qual checking must * be done in the child paths. This means that to have a parameterized * Append path, we must have the exact same parameterization for each * child path; otherwise some children might be failing to check the * moved-down quals. To make them match up, we can try to increase the * parameterization of lesser-parameterized paths. */ foreach(l, all_child_outers) { Relids required_outer = (Relids) lfirst(l); ListCell *lcr; /* Select the child paths for an Append with this parameterization */ subpaths = NIL; subpaths_valid = true; foreach(lcr, live_childrels) { RelOptInfo *childrel = (RelOptInfo *) lfirst(lcr); Path *subpath; if (childrel->pathlist == NIL) { /* failed to make a suitable path for this child */ subpaths_valid = false; break; } subpath = get_cheapest_parameterized_child_path(root, childrel, required_outer); if (subpath == NULL) { /* failed to make a suitable path for this child */ subpaths_valid = false; break; } accumulate_append_subpath(subpath, &subpaths, NULL); } if (subpaths_valid) add_path(rel, (Path *) create_append_path(root, rel, subpaths, NIL, required_outer, 0, false, partitioned_rels, -1)); } }
測試腳本如下
testdb=# explain verbose select * from t_hash_partition where c1 = 1 OR c1 = 2; QUERY PLAN ------------------------------------------------------------------------------------- Append (cost=0.00..30.53 rows=6 width=200) -> Seq Scan on public.t_hash_partition_1 (cost=0.00..15.25 rows=3 width=200) Output: t_hash_partition_1.c1, t_hash_partition_1.c2, t_hash_partition_1.c3 Filter: ((t_hash_partition_1.c1 = 1) OR (t_hash_partition_1.c1 = 2)) -> Seq Scan on public.t_hash_partition_3 (cost=0.00..15.25 rows=3 width=200) Output: t_hash_partition_3.c1, t_hash_partition_3.c2, t_hash_partition_3.c3 Filter: ((t_hash_partition_3.c1 = 1) OR (t_hash_partition_3.c1 = 2)) (7 rows)
啟動gdb,設(shè)置斷點
(gdb) b set_rel_pathlist Breakpoint 1 at 0x796823: file allpaths.c, line 425. (gdb) c Continuing. Breakpoint 1, set_rel_pathlist (root=0x1f1e400, rel=0x1efaba0, rti=1, rte=0x1efa3d0) at allpaths.c:425 425 if (IS_DUMMY_REL(rel)) (gdb)
通過rte->inh判斷是否分區(qū)表或者UNION ALL
(gdb) p rte->inh $1 = true (gdb)
進入set_append_rel_pathlist函數(shù)
(gdb) n 429 else if (rte->inh) (gdb) 432 set_append_rel_pathlist(root, rel, rti, rte); (gdb) step set_append_rel_pathlist (root=0x1f1e400, rel=0x1efaba0, rti=1, rte=0x1efa3d0) at allpaths.c:1296 1296 int parentRTindex = rti;
遍歷子關(guān)系
(gdb) n 1297 List *live_childrels = NIL; (gdb) 1304 foreach(l, root->append_rel_list) (gdb) (gdb) p *root->append_rel_list $2 = {type = T_List, length = 6, head = 0x1fc1f98, tail = 0x1fc2ae8}
獲取AppendRelInfo,判斷父關(guān)系是否正在處理的父關(guān)系
(gdb) n 1306 AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l); (gdb) 1312 if (appinfo->parent_relid != parentRTindex) (gdb) p *appinfo $3 = {type = T_AppendRelInfo, parent_relid = 1, child_relid = 3, parent_reltype = 16988, child_reltype = 16991, translated_vars = 0x1fc1e60, parent_reloid = 16986} (gdb)
獲取子關(guān)系的相關(guān)信息,遞歸調(diào)用set_rel_pathlist
(gdb) n 1316 childRTindex = appinfo->child_relid; (gdb) 1317 childRTE = root->simple_rte_array[childRTindex]; (gdb) 1318 childrel = root->simple_rel_array[childRTindex]; (gdb) 1326 if (!rel->consider_parallel) (gdb) 1332 set_rel_pathlist(root, childrel, childRTindex, childRTE); (gdb) (gdb) Breakpoint 1, set_rel_pathlist (root=0x1f1e400, rel=0x1f1c8a0, rti=3, rte=0x1efa658) at allpaths.c:425 425 if (IS_DUMMY_REL(rel)) (gdb) finish Run till exit from #0 set_rel_pathlist (root=0x1f1e400, rel=0x1f1c8a0, rti=3, rte=0x1efa658) at allpaths.c:425 set_append_rel_pathlist (root=0x1f1e400, rel=0x1efaba0, rti=1, rte=0x1efa3d0) at allpaths.c:1337
如為虛擬關(guān)系,則忽略之
1337 if (IS_DUMMY_REL(childrel))
該子關(guān)系不是虛擬關(guān)系,繼續(xù)處理,加入到rel->partitioned_child_rels和live_childrels鏈表中
(gdb) n 1341 if (rel->part_scheme) (gdb) 1344 list_copy(childrel->partitioned_child_rels)); (gdb) 1343 list_concat(rel->partitioned_child_rels, (gdb) 1342 rel->partitioned_child_rels = (gdb) 1349 live_childrels = lappend(live_childrels, childrel); (gdb) p *rel->partitioned_child_rels $4 = {type = T_IntList, length = 1, head = 0x1fc4d78, tail = 0x1fc4d78} (gdb) p rel->partitioned_child_rels->head->data.int_value $6 = 1 (gdb) (gdb) n 1304 foreach(l, root->append_rel_list) (gdb) p live_childrels $7 = (List *) 0x1fd0a60 (gdb) p *live_childrels $8 = {type = T_List, length = 1, head = 0x1fd0a38, tail = 0x1fd0a38} (gdb) p *(Node *)live_childrels->head->data.ptr_value $9 = {type = T_RelOptInfo} (gdb) p *(RelOptInfo *)live_childrels->head->data.ptr_value $10 = {type = T_RelOptInfo, reloptkind = RELOPT_OTHER_MEMBER_REL, relids = 0x1fc3590, rows = 3, consider_startup = false, consider_param_startup = false, consider_parallel = true, reltarget = 0x1fc35b0, pathlist = 0x1fd0940, ppilist = 0x0, partial_pathlist = 0x1fd09a0, cheapest_startup_path = 0x1fc44f8, cheapest_total_path = 0x1fc44f8, cheapest_unique_path = 0x0, cheapest_parameterized_paths = 0x1fd0a00, direct_lateral_relids = 0x0, lateral_relids = 0x0, relid = 3, reltablespace = 0, rtekind = RTE_RELATION, min_attr = -7, max_attr = 3, attr_needed = 0x1fc2e38, attr_widths = 0x1fc3628, lateral_vars = 0x0, lateral_referencers = 0x0, indexlist = 0x0, statlist = 0x0, pages = 10, tuples = 350, 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 = 0x1fc68d8, baserestrictcost = {startup = 0, per_tuple = 0.0050000000000000001}, baserestrict_min_security = 0, joininfo = 0x0, has_eclass_joins = false, consider_partitionwise_join = false, top_parent_relids = 0x1fc3608, part_scheme = 0x0, nparts = 0, boundinfo = 0x0, partition_qual = 0x0, part_rels = 0x0, partexprs = 0x0, nullable_partexprs = 0x0, partitioned_child_rels = 0x0}
對于虛擬子關(guān)系(上一節(jié)介紹的被裁剪的分區(qū)),直接跳過
(gdb) n 1306 AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l); (gdb) 1312 if (appinfo->parent_relid != parentRTindex) (gdb) 1316 childRTindex = appinfo->child_relid; (gdb) 1317 childRTE = root->simple_rte_array[childRTindex]; (gdb) 1318 childrel = root->simple_rel_array[childRTindex]; (gdb) 1326 if (!rel->consider_parallel) (gdb) 1332 set_rel_pathlist(root, childrel, childRTindex, childRTE); (gdb) 1337 if (IS_DUMMY_REL(childrel)) (gdb) 1338 continue;
設(shè)置斷點,進入add_paths_to_append_rel函數(shù)
(gdb) b add_paths_to_append_rel Breakpoint 2 at 0x797d88: file allpaths.c, line 1372. (gdb) c Continuing. Breakpoint 2, add_paths_to_append_rel (root=0x1f1cdb8, rel=0x1fc1800, live_childrels=0x1fcfb10) at allpaths.c:1372 1372 List *subpaths = NIL; (gdb)
輸入?yún)?shù),其中rel是父關(guān)系,live_childrels是經(jīng)裁剪后仍存活的分區(qū)(子關(guān)系)
(gdb) n 1373 bool subpaths_valid = true; (gdb) p *rel $11 = {type = T_RelOptInfo, reloptkind = RELOPT_BASEREL, relids = 0x1fc1a18, rows = 6, consider_startup = false, consider_param_startup = false, consider_parallel = true, reltarget = 0x1fc1a38, 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 = 0x1fc1a90, attr_widths = 0x1fc1b28, lateral_vars = 0x0, lateral_referencers = 0x0, indexlist = 0x0, statlist = 0x0, pages = 0, tuples = 6, 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 = 0x1fc3e20, baserestrictcost = {startup = 0, per_tuple = 0}, baserestrict_min_security = 0, joininfo = 0x0, has_eclass_joins = false, consider_partitionwise_join = false, top_parent_relids = 0x0, part_scheme = 0x1fc1b80, nparts = 6, boundinfo = 0x1fc1d30, partition_qual = 0x0, part_rels = 0x1fc2000, partexprs = 0x1fc1f08, nullable_partexprs = 0x1fc1fe0, partitioned_child_rels = 0x1fc3ea0}
初始化變量
(gdb) n 1374 List *partial_subpaths = NIL; (gdb) 1375 List *pa_partial_subpaths = NIL; (gdb) 1376 List *pa_nonpartial_subpaths = NIL; (gdb) 1377 bool partial_subpaths_valid = true; (gdb) 1379 List *all_child_pathkeys = NIL; (gdb) 1380 List *all_child_outers = NIL; (gdb) 1382 List *partitioned_rels = NIL; (gdb) 1383 double partial_rows = -1; (gdb) 1386 pa_subpaths_valid = enable_parallel_append && rel->consider_parallel; (gdb) 1404 if (rel->part_scheme != NULL) (gdb) p pa_subpaths_valid $17 = true
構(gòu)建partitioned_rels鏈表
(gdb) n 1406 if (IS_SIMPLE_REL(rel)) (gdb) 1407 partitioned_rels = list_make1(rel->partitioned_child_rels); (gdb) 1433 Assert(list_length(partitioned_rels) >= 1); (gdb) 1441 foreach(l, live_childrels) (gdb) p *partitioned_rels $18 = {type = T_List, length = 1, head = 0x1fcff40, tail = 0x1fcff40}
開始遍歷live_childrels,對于每一個非虛擬子關(guān)系,記錄成本最低的訪問路徑.
如果子關(guān)系存在非參數(shù)化的總成本最低的訪問路徑,添加此路徑到我們?yōu)楦戈P(guān)系構(gòu)建的非參數(shù)化的Append訪問路徑中.
(gdb) n 1443 RelOptInfo *childrel = lfirst(l); (gdb) 1445 Path *cheapest_partial_path = NULL; (gdb) 1451 if (rel->rtekind == RTE_SUBQUERY && childrel->partitioned_child_rels != NIL) (gdb) 1463 if (childrel->pathlist != NIL && (gdb) 1464 childrel->cheapest_total_path->param_info == NULL) (gdb) 1463 if (childrel->pathlist != NIL && (gdb) 1465 accumulate_append_subpath(childrel->cheapest_total_path,
同樣的思路,處理并行處理中的部分計劃
(gdb) n 1471 if (childrel->partial_pathlist != NIL) (gdb) 1473 cheapest_partial_path = linitial(childrel->partial_pathlist); (gdb) 1474 accumulate_append_subpath(cheapest_partial_path,
同樣的,處理并行append混合并行/非并行訪問路徑
(gdb) n 1486 Path *nppath = NULL; (gdb) 1489 get_cheapest_parallel_safe_total_inner(childrel->pathlist); (gdb) 1488 nppath = (gdb) 1491 if (cheapest_partial_path == NULL && nppath == NULL) (gdb) 1496 else if (nppath == NULL || (gdb) 1498 cheapest_partial_path->total_cost < nppath->total_cost)) (gdb) 1497 (cheapest_partial_path != NULL && (gdb) 1501 Assert(cheapest_partial_path != NULL); (gdb) 1502 accumulate_append_subpath(cheapest_partial_path,
收集子關(guān)系所有可用的排序和參數(shù)化路徑鏈表.
(gdb) 1534 foreach(lcp, childrel->pathlist) (gdb) (gdb) n 1536 Path *childpath = (Path *) lfirst(lcp); (gdb) 1537 List *childkeys = childpath->pathkeys; (gdb) 1538 Relids childouter = PATH_REQ_OUTER(childpath); (gdb) 1541 if (childkeys != NIL) (gdb) 1567 if (childouter) (gdb) 1534 foreach(lcp, childrel->pathlist)
繼續(xù)下一個子關(guān)系,完成處理
... (gdb) 1441 foreach(l, live_childrels) (gdb) 1598 if (subpaths_valid)
如存在子關(guān)系的非參數(shù)化訪問路徑,構(gòu)建未排序/未參數(shù)化的Append訪問路徑.
(gdb) n 1599 add_path(rel, (Path *) create_append_path(root, rel, subpaths, NIL, (gdb) p *rel->pathlist Cannot access memory at address 0x0 (gdb) n 1607 if (partial_subpaths_valid) (gdb) p *rel->pathlist $22 = {type = T_List, length = 1, head = 0x1fd0230, tail = 0x1fd0230}
嘗試未排序/未參數(shù)化的部分Append訪問路徑.如可能,構(gòu)建parallel-aware訪問路徑.
... (gdb) 1641 appendpath = create_append_path(root, rel, NIL, partial_subpaths, (gdb) 1650 partial_rows = appendpath->path.rows; (gdb) 1653 add_partial_path(rel, (Path *) appendpath); (gdb)
使用混合的部分和非部分并行的append.
1662 if (pa_subpaths_valid && pa_nonpartial_subpaths != NIL) (gdb)
基于收集的子路徑鍵,構(gòu)建非參數(shù)化的MergeAppend訪問路徑
1701 if (subpaths_valid) (gdb) 1702 generate_mergeappend_paths(root, rel, live_childrels,
完成調(diào)用
(gdb) 1719 foreach(l, all_child_outers) (gdb) 1757 } (gdb) set_append_rel_pathlist (root=0x1f1cdb8, rel=0x1fc1800, rti=1, rte=0x1efa3d0) at allpaths.c:1354 1354 } (gdb) p *rel->pathlist $23 = {type = T_List, length = 1, head = 0x1fd0230, tail = 0x1fd0230} (gdb) (gdb) p *(Node *)rel->pathlist->head->data.ptr_value $24 = {type = T_AppendPath} (gdb) p *(AppendPath *)rel->pathlist->head->data.ptr_value $25 = {path = {type = T_AppendPath, pathtype = T_Append, parent = 0x1fc1800, pathtarget = 0x1fc1a38, param_info = 0x0, parallel_aware = false, parallel_safe = true, parallel_workers = 0, rows = 6, startup_cost = 0, total_cost = 30.530000000000001, pathkeys = 0x0}, partitioned_rels = 0x1fd01f8, subpaths = 0x1fcffc8, first_partial_path = 2}
結(jié)束調(diào)用
(gdb) n set_rel_pathlist (root=0x1f1cdb8, rel=0x1fc1800, rti=1, rte=0x1efa3d0) at allpaths.c:495 495 if (rel->reloptkind == RELOPT_BASEREL && (gdb) 496 bms_membership(root->all_baserels) != BMS_SINGLETON) (gdb) 495 if (rel->reloptkind == RELOPT_BASEREL && (gdb) 504 if (set_rel_pathlist_hook) (gdb) 508 set_cheapest(rel); (gdb) 513 } (gdb)
感謝各位的閱讀!關(guān)于“PostgreSQL如何為append relation構(gòu)建訪問路徑”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。