溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

PostgreSQL中sort_inner_and_outer函數(shù)分析

發(fā)布時(shí)間:2021-11-10 16:08:46 來源:億速云 閱讀:127 作者:iii 欄目:關(guān)系型數(shù)據(jù)庫

這篇文章主要講解了“PostgreSQL中sort_inner_and_outer函數(shù)分析”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“PostgreSQL中sort_inner_and_outer函數(shù)分析”吧!


merge join的算法實(shí)現(xiàn)偽代碼如下:
READ data_set_1 SORT BY JOIN KEY TO temp_ds1
READ data_set_2 SORT BY JOIN KEY TO temp_ds2
READ ds1_row FROM temp_ds1
READ ds2_row FROM temp_ds2
WHILE NOT eof ON temp_ds1,temp_ds2 LOOP
IF ( temp_ds1.key = temp_ds2.key ) OUTPUT JOIN ds1_row,ds2_row
ELSIF ( temp_ds1.key <= temp_ds2.key ) READ ds1_row FROM temp_ds1
ELSIF ( temp_ds1.key => temp_ds2.key ) READ ds2_row FROM temp_ds2
END LOOP

一、數(shù)據(jù)結(jié)構(gòu)

Cost相關(guān)
注意:實(shí)際使用的參數(shù)值通過系統(tǒng)配置文件定義,而不是這里的常量定義!

 typedef double Cost; /* execution cost (in page-access units) */

 /* defaults for costsize.c's Cost parameters */
 /* NB: cost-estimation code should use the variables, not these constants! */
 /* 注意:實(shí)際值通過系統(tǒng)配置文件定義,而不是這里的常量定義! */
 /* If you change these, update backend/utils/misc/postgresql.sample.conf */
 #define DEFAULT_SEQ_PAGE_COST  1.0       //順序掃描page的成本
 #define DEFAULT_RANDOM_PAGE_COST  4.0      //隨機(jī)掃描page的成本
 #define DEFAULT_CPU_TUPLE_COST  0.01     //處理一個(gè)元組的CPU成本
 #define DEFAULT_CPU_INDEX_TUPLE_COST 0.005   //處理一個(gè)索引元組的CPU成本
 #define DEFAULT_CPU_OPERATOR_COST  0.0025    //執(zhí)行一次操作或函數(shù)的CPU成本
 #define DEFAULT_PARALLEL_TUPLE_COST 0.1    //并行執(zhí)行,從一個(gè)worker傳輸一個(gè)元組到另一個(gè)worker的成本
 #define DEFAULT_PARALLEL_SETUP_COST  1000.0  //構(gòu)建并行執(zhí)行環(huán)境的成本
 
 #define DEFAULT_EFFECTIVE_CACHE_SIZE  524288    /*先前已有介紹, measured in pages */

 double      seq_page_cost = DEFAULT_SEQ_PAGE_COST;
 double      random_page_cost = DEFAULT_RANDOM_PAGE_COST;
 double      cpu_tuple_cost = DEFAULT_CPU_TUPLE_COST;
 double      cpu_index_tuple_cost = DEFAULT_CPU_INDEX_TUPLE_COST;
 double      cpu_operator_cost = DEFAULT_CPU_OPERATOR_COST;
 double      parallel_tuple_cost = DEFAULT_PARALLEL_TUPLE_COST;
 double      parallel_setup_cost = DEFAULT_PARALLEL_SETUP_COST;
 
 int         effective_cache_size = DEFAULT_EFFECTIVE_CACHE_SIZE;
 Cost        disable_cost = 1.0e10;//1后面10個(gè)0,通過設(shè)置一個(gè)巨大的成本,讓優(yōu)化器自動放棄此路徑
 
 int         max_parallel_workers_per_gather = 2;//每次gather使用的worker數(shù)

二、源碼解讀

sort_inner_and_outer函數(shù)嘗試構(gòu)造merge join訪問路徑.
構(gòu)造過程中的成本估算實(shí)現(xiàn)函數(shù)initial_cost_mergejoin和final_cost_mergejoin在下一節(jié)介紹.

//------------------------------------------------ sort_inner_and_outer

/*
 * sort_inner_and_outer
 *    Create mergejoin join paths by explicitly sorting both the outer and
 *    inner join relations on each available merge ordering.
 *    顯式排序outer和inner表,創(chuàng)建mergejoin連接路徑.
 *
 * 'joinrel' is the join relation
 * joinrel-連接后新生成的relation
 * 'outerrel' is the outer join relation
 * outerrel-參與連接的outer relation(俗稱外表,驅(qū)動表)
 * 'innerrel' is the inner join relation
 * innerrel-參與連接的inner relation(俗稱內(nèi)表)
 * 'jointype' is the type of join to do
 * jointype-連接類型
 * 'extra' contains additional input values
 * extra-額外的輸入?yún)?shù)
 */
static void
sort_inner_and_outer(PlannerInfo *root,
                     RelOptInfo *joinrel,
                     RelOptInfo *outerrel,
                     RelOptInfo *innerrel,
                     JoinType jointype,
                     JoinPathExtraData *extra)
{
    JoinType    save_jointype = jointype;
    Path       *outer_path;
    Path       *inner_path;
    Path       *cheapest_partial_outer = NULL;
    Path       *cheapest_safe_inner = NULL;
    List       *all_pathkeys;
    ListCell   *l;

    /*
     * We only consider the cheapest-total-cost input paths, since we are
     * assuming here that a sort is required.  We will consider
     * cheapest-startup-cost input paths later, and only if they don't need a
     * sort.
     * 由于我們假定排序是必須的,只考慮總成本最低的路徑。
     * 后續(xù)會嘗試啟動成本最低的路徑,并且只在它們不需要排序的情況下。
     *
     * This function intentionally does not consider parameterized input
     * paths, except when the cheapest-total is parameterized.  If we did so,
     * we'd have a combinatorial explosion of mergejoin paths of dubious
     * value.  This interacts with decisions elsewhere that also discriminate
     * against mergejoins with parameterized inputs; see comments in
     * src/backend/optimizer/README.
     * 該函數(shù)特意不考慮參數(shù)化輸入路徑,除非成本最低路徑是參數(shù)化的。
     * 如果考慮了參數(shù)化輸入路徑,mergejoin將有一個(gè)數(shù)量巨大的組合,成本上劃不來。
     * 而且這將與其他地方的決策相互作用,這些決策同樣會"歧視"使用參數(shù)化輸入的mergejoin;
     * 請參閱src/backend/optimizer/README中的注釋。
     */
    outer_path = outerrel->cheapest_total_path;
    inner_path = innerrel->cheapest_total_path;

    /*
     * If either cheapest-total path is parameterized by the other rel, we
     * can't use a mergejoin.  (There's no use looking for alternative input
     * paths, since these should already be the least-parameterized available
     * paths.)
     * 如果任意一個(gè)總成本最低的路徑使用了參數(shù)化訪問路徑,那么不能使用合并連接。
     * (沒有必要尋找替代的輸入路徑,因?yàn)檫@些路徑已經(jīng)是參數(shù)化最少的可用路徑了。)
     */
    if (PATH_PARAM_BY_REL(outer_path, innerrel) ||
        PATH_PARAM_BY_REL(inner_path, outerrel))
        return;

    /*
     * If unique-ification is requested, do it and then handle as a plain
     * inner join.
     * 如果需要保證唯一,創(chuàng)建唯一性訪問路徑并設(shè)置為JOIN_INNER連接類型
     */
    if (jointype == JOIN_UNIQUE_OUTER)
    {
        outer_path = (Path *) create_unique_path(root, outerrel,
                                                 outer_path, extra->sjinfo);
        Assert(outer_path);
        jointype = JOIN_INNER;
    }
    else if (jointype == JOIN_UNIQUE_INNER)
    {
        inner_path = (Path *) create_unique_path(root, innerrel,
                                                 inner_path, extra->sjinfo);
        Assert(inner_path);
        jointype = JOIN_INNER;
    }

    /*
     * If the joinrel is parallel-safe, we may be able to consider a partial
     * merge join.  However, we can't handle JOIN_UNIQUE_OUTER, because the
     * outer path will be partial, and therefore we won't be able to properly
     * guarantee uniqueness.  Similarly, we can't handle JOIN_FULL and
     * JOIN_RIGHT, because they can produce false null extended rows.  Also,
     * the resulting path must not be parameterized.
     * 如果連接可以并行執(zhí)行,那么可以考慮并行合并連接。
     * 但是,PG不能處理JOIN_UNIQUE_OUTER,因?yàn)橥獠柯窂绞遣糠值?,因此不能正確地保證惟一性。
     * 類似地,PG不能處理JOIN_FULL和JOIN_RIGHT,因?yàn)樗鼈儠a(chǎn)生假空擴(kuò)展行。
     * 此外,生成的路徑不能被參數(shù)化。
     */
    if (joinrel->consider_parallel &&
        save_jointype != JOIN_UNIQUE_OUTER &&
        save_jointype != JOIN_FULL &&
        save_jointype != JOIN_RIGHT &&
        outerrel->partial_pathlist != NIL &&
        bms_is_empty(joinrel->lateral_relids))
    {
        cheapest_partial_outer = (Path *) linitial(outerrel->partial_pathlist);

        if (inner_path->parallel_safe)
            cheapest_safe_inner = inner_path;
        else if (save_jointype != JOIN_UNIQUE_INNER)
            cheapest_safe_inner =
                get_cheapest_parallel_safe_total_inner(innerrel->pathlist);
    }

    /*
     * Each possible ordering of the available mergejoin clauses will generate
     * a differently-sorted result path at essentially the same cost.  We have
     * no basis for choosing one over another at this level of joining, but
     * some sort orders may be more useful than others for higher-level
     * mergejoins, so it's worth considering multiple orderings.
     * 可用的mergejoin子句的每一種可能的排序都將以基本相同的代價(jià)生成不同的排序結(jié)果路徑。
     * 在這種連接級別上,我們沒有選擇一個(gè)排序的基礎(chǔ),但是對于更高層的合并,
     * 某些排序順序可能比其他更有用,因此值得考慮多種排序的順序。
     *
     * Actually, it's not quite true that every mergeclause ordering will
     * generate a different path order, because some of the clauses may be
     * partially redundant (refer to the same EquivalenceClasses).  Therefore,
     * what we do is convert the mergeclause list to a list of canonical
     * pathkeys, and then consider different orderings of the pathkeys.
     * 實(shí)際上,并不是每個(gè)mergeclause都會生成不同的路徑順序,因?yàn)橛行┳泳淇赡苁遣糠秩哂嗟?請參閱等價(jià)類)。
     * 因此,要做的是將mergeclause列表轉(zhuǎn)換為一個(gè)規(guī)范路徑鍵列表,然后考慮路徑鍵的不同順序。
     *
     * Generating a path for *every* permutation of the pathkeys doesn't seem
     * like a winning strategy; the cost in planning time is too high. For
     * now, we generate one path for each pathkey, listing that pathkey first
     * and the rest in random order.  This should allow at least a one-clause
     * mergejoin without re-sorting against any other possible mergejoin
     * partner path.  But if we've not guessed the right ordering of secondary
     * keys, we may end up evaluating clauses as qpquals when they could have
     * been done as mergeclauses.  (In practice, it's rare that there's more
     * than two or three mergeclauses, so expending a huge amount of thought
     * on that is probably not worth it.)
     * 為每個(gè)路徑鍵的排列生成路徑似乎不是一個(gè)好的策略,因?yàn)橛?jì)劃時(shí)間成本太高了。
     * 現(xiàn)在,我們?yōu)槊總€(gè)pathkey生成一個(gè)路徑,首先列出這個(gè)pathkey,然后按隨機(jī)順序列出其余的路徑。
     * 這應(yīng)該至少允許一個(gè)單子句的mergejoin,而不需要對任何其他可能的mergejoin路徑進(jìn)行重新排序。
     * 但如果沒有正確判斷二級鍵的順序,那么可能會以qpquals的形式計(jì)算條件子句,而這些子句本可以作為mergeclauses完成。
     * (實(shí)際上,很少有兩個(gè)或三個(gè)以上的mergeclauses,所以花大量時(shí)間在這上面可能不值得)。
     *
     * The pathkey order returned by select_outer_pathkeys_for_merge() has
     * some heuristics behind it (see that function), so be sure to try it
     * exactly as-is as well as making variants.
     * 通過調(diào)用select_outer_pathkeys_for_merge函數(shù)返回的排序鍵順序pathkey
     * 背后有一些啟發(fā)式函數(shù)(請參閱該函數(shù)),所以一定要按原樣嘗試它,并創(chuàng)建變體。
     */
    all_pathkeys = select_outer_pathkeys_for_merge(root,
                                                   extra->mergeclause_list,
                                                   joinrel);

    foreach(l, all_pathkeys)//遍歷所有可用的排序鍵
    {
        List       *front_pathkey = (List *) lfirst(l);
        List       *cur_mergeclauses;
        List       *outerkeys;
        List       *innerkeys;
        List       *merge_pathkeys;

        /* Make a pathkey list with this guy first */
        if (l != list_head(all_pathkeys))
            outerkeys = lcons(front_pathkey,
                              list_delete_ptr(list_copy(all_pathkeys),
                                              front_pathkey));
        else
            outerkeys = all_pathkeys;   /* no work at first one... */

        /* Sort the mergeclauses into the corresponding ordering */
        cur_mergeclauses =
            find_mergeclauses_for_outer_pathkeys(root,
                                                 outerkeys,
                                                 extra->mergeclause_list);

        /* Should have used them all... */
        Assert(list_length(cur_mergeclauses) == list_length(extra->mergeclause_list));

        /* Build sort pathkeys for the inner side */
        innerkeys = make_inner_pathkeys_for_merge(root,
                                                  cur_mergeclauses,
                                                  outerkeys);

        /* Build pathkeys representing output sort order */
        merge_pathkeys = build_join_pathkeys(root, joinrel, jointype,
                                             outerkeys);

        /*
         * And now we can make the path.
         *
         * Note: it's possible that the cheapest paths will already be sorted
         * properly.  try_mergejoin_path will detect that case and suppress an
         * explicit sort step, so we needn't do so here.
         * 注意:成本最低的路徑可能已被正確排序。
         *      try_mergejoin_path將檢測這種情況并禁止顯式排序步驟,因此在這里不需要這樣做。
         */
        try_mergejoin_path(root,
                           joinrel,
                           outer_path,
                           inner_path,
                           merge_pathkeys,
                           cur_mergeclauses,
                           outerkeys,
                           innerkeys,
                           jointype,
                           extra,
                           false);

        /*
         * If we have partial outer and parallel safe inner path then try
         * partial mergejoin path.
         * 并行處理
         */
        if (cheapest_partial_outer && cheapest_safe_inner)
            try_partial_mergejoin_path(root,
                                       joinrel,
                                       cheapest_partial_outer,
                                       cheapest_safe_inner,
                                       merge_pathkeys,
                                       cur_mergeclauses,
                                       outerkeys,
                                       innerkeys,
                                       jointype,
                                       extra);
    }
}


//----------------------------------- try_mergejoin_path
/*
 * try_mergejoin_path
 *    Consider a merge join path; if it appears useful, push it into
 *    the joinrel's pathlist via add_path().
 *    嘗試merge join path,如可用,則把該路徑通過add_path函數(shù)放在joinrel的pathlist鏈表中
 */
static void
try_mergejoin_path(PlannerInfo *root,
                   RelOptInfo *joinrel,
                   Path *outer_path,
                   Path *inner_path,
                   List *pathkeys,
                   List *mergeclauses,
                   List *outersortkeys,
                   List *innersortkeys,
                   JoinType jointype,
                   JoinPathExtraData *extra,
                   bool is_partial)
{
    Relids      required_outer;
    JoinCostWorkspace workspace;

    if (is_partial)
    {
        try_partial_mergejoin_path(root,
                                   joinrel,
                                   outer_path,
                                   inner_path,
                                   pathkeys,
                                   mergeclauses,
                                   outersortkeys,
                                   innersortkeys,
                                   jointype,
                                   extra);//并行執(zhí)行
        return;
    }

    /*
     * Check to see if proposed path is still parameterized, and reject if the
     * parameterization wouldn't be sensible.
     * 檢查建議的路徑是否仍然參數(shù)化,如果參數(shù)化不合理,則舍棄。
     */
    required_outer = calc_non_nestloop_required_outer(outer_path,
                                                      inner_path);
    if (required_outer &&
        !bms_overlap(required_outer, extra->param_source_rels))
    {
        /* Waste no memory when we reject a path here */
        bms_free(required_outer);
        return;
    }

    /*
     * If the given paths are already well enough ordered, we can skip doing
     * an explicit sort.
     * 如果給定的路徑已完成排序,則跳過顯式排序.
     */
    if (outersortkeys &&
        pathkeys_contained_in(outersortkeys, outer_path->pathkeys))
        outersortkeys = NIL;
    if (innersortkeys &&
        pathkeys_contained_in(innersortkeys, inner_path->pathkeys))
        innersortkeys = NIL;

    /*
     * See comments in try_nestloop_path().
     */
    initial_cost_mergejoin(root, &workspace, jointype, mergeclauses,
                           outer_path, inner_path,
                           outersortkeys, innersortkeys,
                           extra);//初始化mergejoin

    if (add_path_precheck(joinrel,
                          workspace.startup_cost, workspace.total_cost,
                          pathkeys, required_outer))//執(zhí)行前置檢查
    {
        add_path(joinrel, (Path *)
                 create_mergejoin_path(root,
                                       joinrel,
                                       jointype,
                                       &workspace,
                                       extra,
                                       outer_path,
                                       inner_path,
                                       extra->restrictlist,
                                       pathkeys,
                                       required_outer,
                                       mergeclauses,
                                       outersortkeys,
                                       innersortkeys));//創(chuàng)建并添加路徑
    }
    else
    {
        /* Waste no memory when we reject a path here */
        bms_free(required_outer);
    }
}


//----------------------- create_mergejoin_path
 
 /*
  * create_mergejoin_path
  *    Creates a pathnode corresponding to a mergejoin join between
  *    two relations
  *    創(chuàng)建merge join訪問路徑
  *
  * 'joinrel' is the join relation
  * joinrel-連接后新生成的relation
  * 'jointype' is the type of join required
  * jointype-連接類型
  * 'workspace' is the result from initial_cost_mergejoin
  * workspace-通過函數(shù)initial_cost_mergejoin返回的結(jié)果
  * 'extra' contains various information about the join
  * extra-額外的輸入?yún)?shù)
  * 'outer_path' is the outer path
  * outer_path-outer relation訪問路徑
  * 'inner_path' is the inner path
  * inner_path-inner relation訪問路徑
  * 'restrict_clauses' are the RestrictInfo nodes to apply at the join
  * restrict_clauses-約束條件子句
  * 'pathkeys' are the path keys of the new join path
  * pathkeys-排序鍵
  * 'required_outer' is the set of required outer rels
  * required_outer-如需要outer rels,則在此保存relids
  * 'mergeclauses' are the RestrictInfo nodes to use as merge clauses
  *      (this should be a subset of the restrict_clauses list)
  * mergeclauses-合并連接條件子句
  * 'outersortkeys' are the sort varkeys for the outer relation
  * outersortkeys-outer relation的排序鍵
  * 'innersortkeys' are the sort varkeys for the inner relation
  * innersortkeys-inner relation的排序鍵
  */
 MergePath *
 create_mergejoin_path(PlannerInfo *root,
                       RelOptInfo *joinrel,
                       JoinType jointype,
                       JoinCostWorkspace *workspace,
                       JoinPathExtraData *extra,
                       Path *outer_path,
                       Path *inner_path,
                       List *restrict_clauses,
                       List *pathkeys,
                       Relids required_outer,
                       List *mergeclauses,
                       List *outersortkeys,
                       List *innersortkeys)
 {
     MergePath  *pathnode = makeNode(MergePath);
 
     pathnode->jpath.path.pathtype = T_MergeJoin;
     pathnode->jpath.path.parent = joinrel;
     pathnode->jpath.path.pathtarget = joinrel->reltarget;
     pathnode->jpath.path.param_info =
         get_joinrel_parampathinfo(root,
                                   joinrel,
                                   outer_path,
                                   inner_path,
                                   extra->sjinfo,
                                   required_outer,
                                   &restrict_clauses);
     pathnode->jpath.path.parallel_aware = false;
     pathnode->jpath.path.parallel_safe = joinrel->consider_parallel &&
         outer_path->parallel_safe && inner_path->parallel_safe;
     /* This is a foolish way to estimate parallel_workers, but for now... */
     pathnode->jpath.path.parallel_workers = outer_path->parallel_workers;
     pathnode->jpath.path.pathkeys = pathkeys;
     pathnode->jpath.jointype = jointype;
     pathnode->jpath.inner_unique = extra->inner_unique;
     pathnode->jpath.outerjoinpath = outer_path;
     pathnode->jpath.innerjoinpath = inner_path;
     pathnode->jpath.joinrestrictinfo = restrict_clauses;
     pathnode->path_mergeclauses = mergeclauses;
     pathnode->outersortkeys = outersortkeys;
     pathnode->innersortkeys = innersortkeys;
     /* pathnode->skip_mark_restore will be set by final_cost_mergejoin */
     /* pathnode->materialize_inner will be set by final_cost_mergejoin */
 
     final_cost_mergejoin(root, pathnode, workspace, extra);//估算成本
 
     return pathnode;
 }

三、跟蹤分析

測試腳本如下

select a.*,b.grbh,b.je 
from t_dwxx a,
    lateral (select t1.dwbh,t1.grbh,t2.je 
     from t_grxx t1 
          inner join t_jfxx t2 on t1.dwbh = a.dwbh and t1.grbh = t2.grbh) b
order by b.dwbh;

啟動gdb,設(shè)置斷點(diǎn)

(gdb) b sort_inner_and_outer
Breakpoint 1 at 0x7af63a: file joinpath.c, line 888.
(gdb) c
Continuing.

Breakpoint 1, sort_inner_and_outer (root=0x1a4a278, joinrel=0x1aa7180, outerrel=0x1a55700, innerrel=0x1a56c30, 
    jointype=JOIN_INNER, extra=0x7ffca933f880) at joinpath.c:888
888     JoinType    save_jointype = jointype;
(gdb)

新生成的joinrel是1號和3號RTE的連接,類型為JOIN_INNER

(gdb) p *joinrel->relids->words
$1 = 10
(gdb) p jointype
$2 = JOIN_INNER

獲取排序鍵,PathKey中的等價(jià)類EC,成員為t_grxx.dwbh和t_dwxx.dwbh

...
(gdb) 
993     all_pathkeys = select_outer_pathkeys_for_merge(root,
(gdb) n
997     foreach(l, all_pathkeys)
(gdb) p *all_pathkeys
$3 = {type = T_List, length = 1, head = 0x1a69490, tail = 0x1a69490}
(gdb) p *(PathKey *)all_pathkeys->head->data.ptr_value
$5 = {type = T_PathKey, pk_eclass = 0x1a60e08, pk_opfamily = 1994, pk_strategy = 1, pk_nulls_first = false}
...
(gdb) set $rt=(RelabelType *)((EquivalenceMember *)$ec->ec_members->head->data.ptr_value)->em_expr
(gdb) p *$rt->arg
$14 = {type = T_Var}
(gdb) p *(Var *)$rt->arg
$15 = {xpr = {type = T_Var}, varno = 3, varattno = 1, vartype = 1043, vartypmod = 14, varcollid = 100, varlevelsup = 0, 
  varnoold = 3, varoattno = 1, location = 208}
(gdb) set $rt2=(RelabelType *)((EquivalenceMember *)$ec->ec_members->head->next->data.ptr_value)->em_expr
(gdb) p *(Var *)$rt2->arg
$16 = {xpr = {type = T_Var}, varno = 1, varattno = 2, vartype = 1043, vartypmod = 24, varcollid = 100, varlevelsup = 0, 
  varnoold = 1, varoattno = 2, location = 218}

開始遍歷all_pathkeys

(gdb) n
999         List       *front_pathkey = (List *) lfirst(l);

獲取連接條件子句,t_dwxx.dwbh=t_grxx.dwbh

(gdb) p *cur_mergeclauses
$17 = {type = T_List, length = 1, head = 0x1a694f0, tail = 0x1a694f0}

構(gòu)建outer和inner relation的排序鍵

(gdb) p *(PathKey *)innerkeys->head->data.ptr_value
$22 = {type = T_PathKey, pk_eclass = 0x1a60e08, pk_opfamily = 1994, pk_strategy = 1, pk_nulls_first = false}
(gdb) p *(PathKey *)merge_pathkeys->head->data.ptr_value
$25 = {type = T_PathKey, pk_eclass = 0x1a60e08, pk_opfamily = 1994, pk_strategy = 1, pk_nulls_first = false}

嘗試merge join,進(jìn)入函數(shù)try_mergejoin_path

(gdb) 
1038            try_mergejoin_path(root,
(gdb) step
try_mergejoin_path (root=0x1a4dcc0, joinrel=0x1a68e20, outer_path=0x1a62288, inner_path=0x1a62320, pathkeys=0x1a694b8, 
    mergeclauses=0x1a69518, outersortkeys=0x1a694b8, innersortkeys=0x1a69578, jointype=JOIN_INNER, extra=0x7ffca933f880, 
    is_partial=false) at joinpath.c:572
572     if (is_partial)

初始merge join成本

...
(gdb) 
615     initial_cost_mergejoin(root, &workspace, jointype, mergeclauses,
(gdb) p workspace
$26 = {startup_cost = 10861.483356195882, total_cost = 11134.203356195881, run_cost = 24.997499999999999, 
  inner_run_cost = 247.72250000000003, inner_rescan_run_cost = 1.3627136827435593e-316, outer_rows = 9999, 
  inner_rows = 100000, outer_skip_rows = 0, inner_skip_rows = 911, numbuckets = 27665584, numbatches = 0, 
  inner_rows_total = 1.3681950446447804e-316}

構(gòu)造merge join

...
(gdb) n
625                  create_mergejoin_path(root,
(gdb) 
624         add_path(joinrel, (Path *)
(gdb) 
644 }
(gdb) p *joinrel->pathlist
$28 = {type = T_List, length = 1, head = 0x1a6a180, tail = 0x1a6a180}
(gdb) p *(Node *)joinrel->pathlist->head->data.ptr_value
$29 = {type = T_MergePath}
(gdb) p *(MergePath *)joinrel->pathlist->head->data.ptr_value
$30 = {jpath = {path = {type = T_MergePath, pathtype = T_MergeJoin, parent = 0x1a68e20, pathtarget = 0x1a69058, 
      param_info = 0x0, parallel_aware = false, parallel_safe = true, parallel_workers = 0, rows = 100000, 
      startup_cost = 10863.760856195882, total_cost = 12409.200856195883, pathkeys = 0x1a694b8}, jointype = JOIN_INNER, 
    inner_unique = false, outerjoinpath = 0x1a62288, innerjoinpath = 0x1a62320, joinrestrictinfo = 0x1a692f8}, 
  path_mergeclauses = 0x1a69518, outersortkeys = 0x1a694b8, innersortkeys = 0x1a69578, skip_mark_restore = false, 
  materialize_inner = false}

完成調(diào)用

(gdb) n
sort_inner_and_outer (root=0x1a4dcc0, joinrel=0x1a68e20, outerrel=0x1a4d700, innerrel=0x1a4d918, jointype=JOIN_INNER, 
    extra=0x7ffca933f880) at joinpath.c:1054
1054            if (cheapest_partial_outer && cheapest_safe_inner)
(gdb) 
997     foreach(l, all_pathkeys)
(gdb) 
1066    }
(gdb) n
add_paths_to_joinrel (root=0x1a4dcc0, joinrel=0x1a68e20, outerrel=0x1a4d700, innerrel=0x1a4d918, jointype=JOIN_INNER, 
    sjinfo=0x7ffca933f970, restrictlist=0x1a692f8) at joinpath.c:279
279     if (mergejoin_allowed)
(gdb) 
280         match_unsorted_outer(root, joinrel, outerrel, innerrel,
...

感謝各位的閱讀,以上就是“PostgreSQL中sort_inner_and_outer函數(shù)分析”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對PostgreSQL中sort_inner_and_outer函數(shù)分析這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI