PostgreSQL 原始碼解讀(76)- 查詢語句#61(獲取成本最低的訪問路徑)

husthxd發表於2018-11-02

本節介紹了standard_planner函式中的fetch_upper_rel和get_cheapest_fractional_path函式,其中fetch_upper_rel函式構建用以表示查詢最佳化器生成的最終關係(用RelOptInfo資料結構表示),get_cheapest_fractional_path函式根據輸入的RelOptInfo資料結構找到成本最低的訪問路徑。

一、原始碼解讀

fetch_upper_rel
從最佳化器資訊中的upper_rels資料中獲取用以表示查詢最佳化器生成的最終關係.

//--------------------------------------------------------------------------- fetch_upper_rel

/*
 * fetch_upper_rel
 *      Build a RelOptInfo describing some post-scan/join query processing,
 *      or return a pre-existing one if somebody already built it.
 *      構建RelOptInfo資料結構,用以表示post-scan/join查詢過程.如已存在,則直接返回.
 *
 * An "upper" relation is identified by an UpperRelationKind and a Relids set.
 * The meaning of the Relids set is not specified here, and very likely will
 * vary for different relation kinds.
 * 一個“上級”關係是由一個UpperRelationKind和一個Relids集合來標識。
 * 在這裡沒有指定Relids集合的含義,並且很可能會因不同的關係型別而不同。
 *
 * Most of the fields in an upper-level RelOptInfo are not used and are not
 * set here (though makeNode should ensure they're zeroes).  We basically only
 * care about fields that are of interest to add_path() and set_cheapest().
 * 上層RelOptInfo中的大多數字段都沒有使用,也沒有在這裡設定(儘管makeNode應該確保它們是NULL)。
 * 基本上只關心add_path()和set_cheap()函式所感興趣的欄位。
 */
RelOptInfo *
fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
{
    RelOptInfo *upperrel;
    ListCell   *lc;

    /*
     * For the moment, our indexing data structure is just a List for each
     * relation kind.  If we ever get so many of one kind that this stops
     * working well, we can improve it.  No code outside this function should
     * assume anything about how to find a particular upperrel.
     * 目前,我們已索引的資料結構只是每個關係型別的連結串列。
     * 這個函式之外的任何程式碼都不應該假定如何找到一個特定的上層關係。
     */

    /* If we already made this upperrel for the query, return it */
    //如果已經構造了該查詢的上層關係,直接返回
    foreach(lc, root->upper_rels[kind])
    {
        upperrel = (RelOptInfo *) lfirst(lc);

        if (bms_equal(upperrel->relids, relids))
            return upperrel;
    }

    upperrel = makeNode(RelOptInfo);
    upperrel->reloptkind = RELOPT_UPPER_REL;
    upperrel->relids = bms_copy(relids);

    /* cheap startup cost is interesting iff not all tuples to be retrieved */
    //低廉的啟動成本在較少的元組返回的情況是比較讓人關心的.
    upperrel->consider_startup = (root->tuple_fraction > 0);//如非全部返回元組,則需要考慮啟動成本
    upperrel->consider_param_startup = false;
    upperrel->consider_parallel = false;    /* 以後可能會有變化;might get changed later */
    upperrel->reltarget = create_empty_pathtarget();
    upperrel->pathlist = NIL;
    upperrel->cheapest_startup_path = NULL;
    upperrel->cheapest_total_path = NULL;
    upperrel->cheapest_unique_path = NULL;
    upperrel->cheapest_parameterized_paths = NIL;

    root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);

    return upperrel;
}

get_cheapest_fractional_path
get_cheapest_fractional_path透過對RelOptInfo中的訪問路徑兩兩比較,獲取成本最低的訪問路徑.


//--------------------------------------------------------------------------- get_cheapest_fractional_path

/*
 * get_cheapest_fractional_path
 *    Find the cheapest path for retrieving a specified fraction of all
 *    the tuples expected to be returned by the given relation.
 *    給定關係,找到成本最低的訪問路徑,該路徑返回返回預期設定元組數。
 * 
 * We interpret tuple_fraction the same way as grouping_planner.
 * 我們用grouping_planner函式來獲取tuple_fraction。
 *
 * We assume set_cheapest() has been run on the given rel.
 * 假定set_cheapest()函式已在給定的關係上執行.
 */
Path *
get_cheapest_fractional_path(RelOptInfo *rel, double tuple_fraction)
{
    Path       *best_path = rel->cheapest_total_path;
    ListCell   *l;

    /* If all tuples will be retrieved, just return the cheapest-total path */
    //如果需要檢索所有元組,則返回總成本最低的訪問路徑
    if (tuple_fraction <= 0.0)
        return best_path;

    /* Convert absolute # of tuples to a fraction; no need to clamp to 0..1 */
    //根據比例給出需返回行數
    if (tuple_fraction >= 1.0 && best_path->rows > 0)
        tuple_fraction /= best_path->rows;

    foreach(l, rel->pathlist)
    {
        Path       *path = (Path *) lfirst(l);
        //best_path的成本比path要低或相同,保留
        if (path == rel->cheapest_total_path ||
            compare_fractional_path_costs(best_path, path, tuple_fraction) <= 0)
            continue;
        //否則,選擇新的訪問路徑
        best_path = path;
    }

    return best_path;
}


//------------------------------------------------------ compare_path_costs

 /*
  * compare_path_fractional_costs
  *    Return -1, 0, or +1 according as path1 is cheaper, the same cost,
  *    or more expensive than path2 for fetching the specified fraction
  *    of the total tuples.
  *    返回值:
  *    -1:相對於path2,path1成本更低;
  *     0:與path2成本相同
  *     1:比path2成本更高
  *
  * If fraction is <= 0 or > 1, we interpret it as 1, ie, we select the
  * path with the cheaper total_cost.
  * 如果fraction≤0或者大於1,則選擇總成本最低的訪問路徑
  */
 int
 compare_fractional_path_costs(Path *path1, Path *path2,
                               double fraction)
 {
     Cost        cost1,
                 cost2;
 
     if (fraction <= 0.0 || fraction >= 1.0)
         return compare_path_costs(path1, path2, TOTAL_COST);
     cost1 = path1->startup_cost +
         fraction * (path1->total_cost - path1->startup_cost);
     cost2 = path2->startup_cost +
         fraction * (path2->total_cost - path2->startup_cost);
     if (cost1 < cost2)
         return -1;
     if (cost1 > cost2)
         return +1;
     return 0;
 }

//--------------------------------------- compare_path_costs
 /*
  * compare_path_costs
  *    Return -1, 0, or +1 according as path1 is cheaper, the same cost,
  *    or more expensive than path2 for the specified criterion.
  * 給定標準,返回比較結果.
  * 返回值:
  *    -1:相對於path2,path1成本更低;
  *     0:與path2成本相同
  *     1:比path2成本更高
  */
 int
 compare_path_costs(Path *path1, Path *path2, CostSelector criterion)
 {
     if (criterion == STARTUP_COST)//啟動成本
     {
         if (path1->startup_cost < path2->startup_cost)
             return -1;
         if (path1->startup_cost > path2->startup_cost)
             return +1;
 
         /*
          * If paths have the same startup cost (not at all unlikely), order
          * them by total cost.
          */
         if (path1->total_cost < path2->total_cost)
             return -1;
         if (path1->total_cost > path2->total_cost)
             return +1;
     }
     else//總成本
     {
         if (path1->total_cost < path2->total_cost)
             return -1;
         if (path1->total_cost > path2->total_cost)
             return +1;
 
         /*
          * If paths have the same total cost, order them by startup cost.
          */
         if (path1->startup_cost < path2->startup_cost)
             return -1;
         if (path1->startup_cost > path2->startup_cost)
             return +1;
     }
     return 0;
 }

二、參考資料

allpaths.c
PG Document:Query Planning

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/6906/viewspace-2374815/,如需轉載,請註明出處,否則將追究法律責任。

相關文章