Перейти к основному содержимому

Функции оценки стоимости индекса

Функции 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

Количество повторений сканирования индекса, которое должно учитываться в оценке стоимости. Обычно это значение больше единицы, когда рассматривается параметризованное сканирование для использования внутри nestloop join. Обратите внимание, что оценка стоимости все равно должна быть рассчитана только на одно сканирование; большее значение 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 должно взиматься за любые операторы сравнения, вызываемые во время обработки индекса (особенно за вычисление собственно условий индекса).

Стоимость доступа должна включать все дисковые и процессорные затраты, связанные со сканированием самого индекса, но не затраты на получение или обработку строк родительской таблицы, которые идентифицируются индексом.

«Начальная стоимость» – это часть общей стоимости сканирования, которая должна быть потрачена до того, как мы начнем получать первый ряд. Для большинства индексов этот параметр можно принять за ноль, но для индексов с высокой начальной стоимостью, возможно, потребуется установить ненулевое значение.

Значение 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.