Chapter 47. 索引開銷計算函數

作者: 由 Tom Lane() 寫於 2000-01-24.

注意: 這些內容最後必須成為關於書寫新的索引訪問方式的更大的一章的一部分.

每種索引訪問模式都必須提供一個用於規劃器/優化器的開銷計算函數. 這個函數的過程 OID 在訪問模式在 pg_am 裡的 記錄的 amcostestimate 字串裡給出.

注意: PostgreSQL 7.0 以前, 使用的是另外一種註冊與索引相關的開銷計算函數的模式.

amcostestimate 函數收到一列 WHERE 子句,這些子句被認為對索引是有用的. 這個函數本身必須返回計算出來的訪問索引的開銷和 WHERE 子句的選擇性( 也就是說,在索引掃瞄過程中主資料表行中要被撿索出來的部分). 對於簡單的情況, 幾乎所有開銷計算器的工作都可以透過調用優化器裡標準的過程來完成, 需要一個 amcostestimate 函數的原因是允許索引訪問模式提供一些索引類型相關的訊息, 這樣就有可能改進標準的計算(預計).

每個 amcostestimate 函數都必須有下面的名字:

void
amcostestimate (Query *root,
                RelOptInfo *rel,
                IndexOptInfo *index,
                List *indexQuals,
                Cost *indexStartupCost,
                Cost *indexTotalCost,
                Selectivity *indexSelectivity,
                double *indexCorrelation);
   

前面四個參數是輸入:

root

被處理的查詢.

rel

索引所處的關係(資料表).

index

索引本身.

indexQuals

索引條件子句列資料表(隱含地 AND);一個 NIL 列資料表資料表明沒有可用的條件.

最後四個參數是透過引用傳遞的輸出:

*indexStartupCost

設置為索引啟動處理的開銷

*indexTotalCost

設置為索引處理的總開銷

*indexSelectivity

設置為索引選擇性

*indexCorrelation

設置索引掃瞄順序和下層資料表的順序之間的相關性係數

請注意開銷計算函數必須用 C 寫,而不是 SQL 或者任何可以用的過程語言. 因為他們必須訪問規劃器/優化器的內部資料結構.

索引訪問開銷應該以src/backend/optimizer/path/costsize.c 裡面使用的單位計算: 一次順序磁盤儲存塊抓取開銷為 1.0, 一次非順序抓取的開銷為 random_page_cost, 並且處理一個索引記錄的開銷通常應該當做 cpu_index_tuple_cost (它是一個可以由用戶調節的優化器參數).另外,應該用一個 cpu_operator_cost 的合適的倍數作為索引處理期間任何激活的比較操作符 (尤其是計算 indexQuals (索引查詢)自己).

訪問開銷應該包含所有與掃瞄索引本身的相關的磁盤和 CPU 開銷, 而不是檢索或處理被索引標識的主資料表索引的開銷.

"啟動開銷"是全部索引開銷中在我們開始抓取第一條記錄之前必須消耗的開銷. 對於大多數索引,這部分可以當做零, 但是一個有著比較高啟動開銷的索引類型可能希望把這個值設置為非零.

indexSelectivity (索引選擇性)應該設置為在索引掃瞄過程中主資料表記錄裡將被檢索出的部分. 如果是一個松索引的場合, 這個數字將明顯地比實際傳遞給給出的資格條件的記錄部分高.

indexCorrelation 應該設置為索引順序和資料表順序的相關性係數(範圍在 -1.0 和 1.0 之間).它用於調整從主資料表中抓取行的開銷的計算.

開銷計算

一次典型的開銷計算器將象下面這樣進行:

  1. 計算和返回基於給出的資格條件的將要訪問的主資料表的行數量. 如果不知到任何索引類型相關的訊息,則使用標準的優化器函數 clauselist_selectivity():

    *indexSelectivity = clauselist_selectivity(root, indexQuals,
                                               lfirsti(rel->relids));
         

  2. 計算(估計)在掃瞄過程中將要被訪問的索引記錄數. 對於許多索引類型,這個數字等於 indexSelectivity 乘以索引裡面的記錄數量, 但是它可以更多. (請注意索引在頁面裡的大小和記錄可以從結構 IndexOptInfo 裡獲得.)

  3. 計算(估計)在掃瞄過程中將要被檢索出的索引頁面數. 這個數字可以只是 indexSelectivity 乘以以頁面數計算的索引的大小.

  4. 計算索引訪問開銷.一個常見的計算器可以這樣做:

        /*
         * (我們一般性的假設是索引頁面將被順序讀入,
         *  因此它們每個的開銷為1.0,沒有 random_page_cose.
         *  同樣,我們計算每條索引記錄的索引條件的開銷.
         *  所有開銷都假設是在掃瞄過程中逐步遞增的.)
         *
         * Our generic assumption is that the index pages will be read
         * sequentially, so they have cost 1.0 each, not random_page_cost.
         * Also, we charge for evaluation of the indexquals at each index tuple.
         * All the costs are assumed to be paid incrementally during the scan.
         */
        cost_qual_eval(&index_qual_cost, indexQuals);
        *indexStartupCost = 0;
        *indexTotalCost = numIndexPages +
            (cpu_index_tuple_cost + index_qual_cost.per_tuple) * numIndexTuples;
         

  5. 計算索引相關性.對於在一個字串上的簡單排序的索引,這個它可以從 pg_statistic 中檢索出來.如果相關性未知,保守的估計是零(不相關).

開銷計算器的例子可以在 src/backend/utils/adt/selfuncs.c 找到.

通常,一個 amcostestimate 函數的 pg_proc 記錄會應該顯示八個參數,所有參數都聲明為 internal (因為它們 的類型都不是 SQL 知道的類型),並且它們的返回類型是 void