支援版本:目前 (17) / 16 / 15 / 14 / 13
開發版本:devel
不支援的版本:12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1

62.6. 索引成本估算函數 #

amcostestimate 函數會收到描述可能索引掃描的資訊,包括已確定可用於索引的 WHERE 和 ORDER BY 子句的清單。它必須傳回存取索引的成本估算,以及 WHERE 子句的選擇性(也就是,索引掃描期間將檢索的父表格列的比例)。對於簡單的情況,成本估算器的大部分工作都可以透過呼叫最佳化工具中的標準例程來完成;擁有 amcostestimate 函數的目的是讓索引存取方法提供索引類型特定的知識,以防可能改進標準估算。

每個 amcostestimate 函數都必須具有以下簽章:

void
amcostestimate (PlannerInfo *root,
                IndexPath *path,
                double loop_count,
                Cost *indexStartupCost,
                Cost *indexTotalCost,
                Selectivity *indexSelectivity,
                double *indexCorrelation,
                double *indexPages);

前三個參數是輸入:

root

計畫器關於正在處理的查詢的資訊。

path

正在考慮的索引存取路徑。除了成本和選擇性值之外,所有欄位都有效。

loop_count

應計入成本估算中的索引掃描重複次數。在考慮用於巢狀迴圈聯接內部的參數化掃描時,這通常會大於一。請注意,成本估算仍然應該只針對一次掃描;較大的 loop_count 表示可能適合允許跨多個掃描的某些快取效應。

最後五個參數是傳址輸出:

*indexStartupCost

設定為索引啟動處理的成本

*indexTotalCost

設定為索引處理的總成本

*indexSelectivity

設定為索引選擇性

*indexCorrelation

設定為索引掃描順序與基礎表格順序之間的相關係數

*indexPages

設定為索引葉節點頁數

請注意,成本估算函數必須以 C 語言編寫,而不是以 SQL 或任何可用的程序語言編寫,因為它們必須存取計畫器/最佳化工具的內部資料結構。

索引存取成本應使用 src/backend/optimizer/path/costsize.c 使用的參數來計算:循序磁碟區塊提取的成本為 seq_page_cost,非循序提取的成本為 random_page_cost,而處理一個索引列的成本通常應視為 cpu_index_tuple_cost。此外,對於索引處理期間調用的任何比較運算符(尤其是索引限定詞本身的評估),應收取適當的 cpu_operator_cost 倍數。

存取成本應包括與掃描索引本身相關的所有磁碟和 CPU 成本,但包括檢索或處理索引識別的父表格列的成本。

啟動成本是在我們可以開始提取第一列之前必須支出的總掃描成本的一部分。對於大多數索引,這可以視為零,但具有高啟動成本的索引類型可能希望將其設定為非零。

indexSelectivity 應設定為索引掃描期間將檢索的父表格列的估計比例。在有損查詢的情況下,這通常會高於實際通過給定限定條件的列的比例。

indexCorrelation 應設定為索引順序和表格順序之間的相關性(範圍在 -1.0 到 1.0 之間)。這用於調整從父表格提取列的成本估算。

indexPages 應設定為葉節點頁數。這用於估算並行索引掃描的工作人員數量。

loop_count 大於一時,傳回的數字應為預期用於索引的任何一次掃描的平均值。

成本估算

一個典型的成本估算器會依照以下步驟進行:

  1. 根據給定的限定條件,估算並返回將被訪問的父表格列的比例。在沒有任何索引類型特定知識的情況下,使用標準最佳化器函式 clauselist_selectivity()

    *indexSelectivity = clauselist_selectivity(root, path->indexquals,
                                               path->indexinfo->rel->relid,
                                               JOIN_INNER, NULL);
    
  2. 估算掃描期間將被訪問的索引列數量。對於許多索引類型,這與 indexSelectivity 乘以索引中的列數相同,但可能更多。(請注意,索引的大小(以頁數和列數表示)可從 path->indexinfo 結構中獲得。)

  3. 估算掃描期間將被檢索的索引頁數。這可能只是 indexSelectivity 乘以索引的大小(以頁數表示)。

  4. 計算索引存取成本。一個通用的估算器可能會這樣做:

    /*
     * Our generic assumption is that the index pages will be read
     * sequentially, so they cost seq_page_cost each, not random_page_cost.
     * Also, we charge for evaluation of the indexquals at each index row.
     * All the costs are assumed to be paid incrementally during the scan.
     */
    cost_qual_eval(&index_qual_cost, path->indexquals, root);
    *indexStartupCost = index_qual_cost.startup;
    *indexTotalCost = seq_page_cost * numIndexPages +
        (cpu_index_tuple_cost + index_qual_cost.per_tuple) * numIndexTuples;
    

    然而,以上並沒有考慮到在重複索引掃描中攤銷索引讀取的情況。

  5. 估算索引相關性。對於單一欄位上的簡單排序索引,可以從 pg_statistic 中檢索到此相關性。如果相關性未知,則保守估計為零(無相關性)。

成本估算函式的範例可以在 src/backend/utils/adt/selfuncs.c 中找到。

提交更正

如果您在文件中發現任何不正確、與您使用特定功能的經驗不符或需要進一步澄清的地方,請使用此表單報告文件問題。