63.6. Funciones de estimación de costos de índices #

A la función amcostestimate se le da información que describe un escaneo de índice posible, incluyendo listas de cláusulas WHERE y ORDER BY que se ha determinado que son utilizables con el índice. Debe devolver estimaciones del costo de acceder al índice y la selectividad de las cláusulas WHERE (es decir, la fracción de filas de la tabla padre que se recuperarán durante el escaneo del índice). Para casos sencillos, casi todo el trabajo del estimador de costos se puede realizar llamando a rutinas estándar en el optimizador; el objetivo de tener una función amcostestimate es permitir que los métodos de acceso a índices proporcionen conocimientos específicos del tipo de índice, en caso de que sea posible mejorar las estimaciones estándar.

Cada función amcostestimate debe tener la firma:

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

Los primeros tres parámetros son entradas:

root

La información del planificador sobre la consulta que se está procesando.

path

La ruta de acceso al índice que se está considerando. Todos los campos, excepto los valores de costo y selectividad, son válidos.

loop_count

El número de repeticiones del escaneo del índice que se debe tener en cuenta en las estimaciones de costos. Esto normalmente será mayor que uno cuando se considera un escaneo parametrizado para su uso en el interior de una unión de bucle anidado (nestloop join). Ten en cuenta que las estimaciones de costos deben seguir siendo para un solo escaneo; un loop_count mayor significa que puede ser apropiado permitir algunos efectos de almacenamiento en caché a través de múltiples escaneos.

Los últimos cinco parámetros son salidas por referencia:

*indexStartupCost

Establecido al costo del procesamiento de inicio del índice (startup cost).

*indexTotalCost

Establecido al costo total del procesamiento del índice.

*indexSelectivity

Establecido a la selectividad del índice.

*indexCorrelation

Establecido al coeficiente de correlación entre el orden de escaneo del índice y el orden de la tabla subyacente.

*indexPages

Establecido al número de páginas hoja del índice.

Ten en cuenta que las funciones de estimación de costos deben escribirse en C, no en SQL o en cualquier lenguaje procedimental disponible, porque deben acceder a las estructuras de datos internas del planificador/optimizador.

Los costos de acceso al índice deben calcularse utilizando los parámetros utilizados por src/backend/optimizer/path/costsize.c: la recuperación secuencial de un bloque de disco tiene el costo seq_page_cost, una recuperación no secuencial tiene el costo random_page_cost, y el costo de procesar una fila de índice generalmente debe tomarse como cpu_index_tuple_cost. Además, se debe cobrar un múltiplo adecuado de cpu_operator_cost por cualquier operador de comparación invocado durante el procesamiento del índice (especialmente la evaluación de las propias indexquals).

Los costos de acceso deben incluir todos los costos de disco y de CPU asociados con el escaneo del índice en sí, pero no los costos de recuperar o procesar las filas de la tabla padre identificadas por el índice.

El costo de inicio (start-up cost) es la parte del costo total del escaneo que debe gastarse antes de que podamos comenzar a recuperar la primera fila. Para la mayoría de los índices, esto se puede considerar como cero, pero un tipo de índice con un alto costo de inicio podría querer establecerlo en un valor distinto de cero.

El parámetro indexSelectivity debe establecerse en la fracción estimada de las filas de la tabla padre que se recuperarán durante el escaneo del índice. En el caso de una consulta con pérdidas, esto será típicamente más alto que la fracción de filas que realmente pasan las condiciones cualificadoras dadas.

El parámetro indexCorrelation debe establecerse en la correlación (que varía entre -1.0 y 1.0) entre el orden del índice y el orden de la tabla. Esto se usa para ajustar la estimación del costo de recuperar filas de la tabla padre.

El parámetro indexPages debe establecerse en el número de páginas hoja. Esto se usa para estimar el número de trabajadores para el escaneo de índice en paralelo.

Cuando loop_count es mayor que uno, los números devueltos deben ser los promedios esperados para cualquier escaneo individual del índice.

Estimación de costos

Un estimador de costos típico procederá de la siguiente manera:

  1. Estima y devuelve la fracción de filas de la tabla padre que se visitarán según las condiciones cualificadoras dadas. A falta de conocimientos específicos sobre el tipo de índice, utiliza la función estándar del optimizador clauselist_selectivity():

    *indexSelectivity = clauselist_selectivity(root, path->indexquals,
                                               path->indexinfo->rel->relid,
                                               JOIN_INNER, NULL);
    

  2. Estima el número de filas de índice que se visitarán durante el escaneo. Para muchos tipos de índice, este es el mismo que indexSelectivity multiplicado por el número de filas del índice, pero podría ser más. (Ten en cuenta que el tamaño del índice en páginas y filas está disponible en la estructura path->indexinfo).

  3. Estima el número de páginas de índice que se recuperarán durante el escaneo. Esto podría ser simplemente indexSelectivity multiplicado por el tamaño del índice en páginas.

  4. Calcula el costo de acceso al índice. Un estimador genérico podría hacer esto:

    /*
     * Nuestra suposición genérica es que las páginas de índice se leerán
     * secuencialmente, por lo que cuestan seq_page_cost cada una, no random_page_cost.
     * Además, cobramos por la evaluación de las indexquals en cada fila de índice.
     * Se supone que todos los costos se pagan de forma incremental durante el escaneo.
     */
    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;
    

    Sin embargo, lo anterior no tiene en cuenta la amortización de las lecturas de índice a lo largo de escaneos de índice repetidos.

  5. Estima la correlación del índice. Para un índice ordenado simple en un solo campo, esto se puede recuperar de pg_statistic. Si no se conoce la correlación, la estimación conservadora es cero (sin correlación).

Se pueden encontrar ejemplos de funciones de estimación de costos en src/backend/utils/adt/selfuncs.c.