2025年9月25日: PostgreSQL 18 釋出!
支援的版本: 當前 (18) / 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

63.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 中找到。

提交更正

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