- 14.2.1. Estadísticas de una sola columna
- 14.2.2. Estadísticas extendidas
14.2.1. Estadísticas de una sola columna
Como vimos en la sección anterior, el planificador de consultas necesita estimar el número de filas recuperadas por una consulta para poder hacer buenas elecciones de planes de consulta. Esta sección proporciona un vistazo rápido a las estadísticas que utiliza el sistema para estas estimaciones.
Un componente de las estadísticas es el número total de entradas en cada tabla e índice, así como el número de bloques de disco ocupados por cada tabla e índice. Esta información se mantiene en la tabla. pg_class
, en las columnas reltuples
y relpages
. Podemos mirarlo con consultas similares a esta:
SELECT relname, relkind, reltuples, relpages FROM pg_class WHERE relname LIKE 'tenk1%'; relname | relkind | reltuples | relpages ----------------------+---------+-----------+---------- tenk1 | r | 10000 | 358 tenk1_hundred | i | 10000 | 30 tenk1_thous_tenthous | i | 10000 | 30 tenk1_unique1 | i | 10000 | 30 tenk1_unique2 | i | 10000 | 30 (5 rows)
Aquí podemos ver que tenk1
contiene 10000 filas, al igual que sus índices, pero los índices son (como era de esperar) mucho más pequeños que la tabla.
Por razones de eficiencia, reltuples
y relpages
no se actualizan sobre la marcha, por lo que suelen contener valores algo desactualizados. Son actualizados por VACUUM
, ANALYZE
y algunos comandos DDL como CREATE INDEX
. A VACUUM
o ANALYZE
operación que no escanea toda la tabla (que es comúnmente el caso) actualizará incrementalmente la reltuples
cuente sobre la base de la parte de la tabla que escaneó, lo que da como resultado un valor aproximado. En cualquier caso, el planificador escalará los valores que encuentre en pg_class
para que coincida con el tamaño de la tabla física actual, obteniendo así una aproximación más cercana.
La mayoría de las consultas recuperan solo una fracción de las filas de una tabla, debido a WHERE
cláusulas que restringen las filas a examinar. Por tanto, el planificador necesita hacer una estimación de la selectividad de WHERE
cláusulas, es decir, la fracción de filas que coinciden con cada condición en el WHERE
cláusula. La información utilizada para esta tarea se almacena en el pg_statistic
catálogo del sistema. Entradas en pg_statistic
son actualizados por el ANALYZE
y VACUUM ANALYZE
comandos, y son siempre aproximados incluso cuando se actualizan recientemente.
En lugar de mirar pg_statistic
directamente, es mejor mirar su vista pg_stats
al examinar las estadísticas manualmente. pg_stats
está diseñado para ser más fácil de leer. Es más, pg_stats
es legible para todos, mientras que pg_statistic
solo es legible por un superusuario. (Esto evita que los usuarios sin privilegios aprendan algo sobre el contenido de las tablas de otras personas a partir de las estadísticas. pg_stats
La vista está restringida para mostrar solo filas sobre tablas que el usuario actual puede leer). Por ejemplo, podríamos hacer:
SELECT attname, inherited, n_distinct, array_to_string(most_common_vals, E'n') as most_common_vals FROM pg_stats WHERE tablename="road"; attname | inherited | n_distinct | most_common_vals ---------+-----------+------------+------------------------------------ name | f | -0.363388 | I- 580 Ramp+ | | | I- 880 Ramp+ | | | Sp Railroad + | | | I- 580 + | | | I- 680 Ramp name | t | -0.284859 | I- 880 Ramp+ | | | I- 580 Ramp+ | | | I- 680 Ramp+ | | | I- 580 + | | | State Hwy 13 Ramp (2 rows)
Tenga en cuenta que se muestran dos filas para la misma columna, una correspondiente a la jerarquía de herencia completa comenzando en el road
mesa (inherited
=t
), y otro que incluye solo el road
la mesa en síinherited
=f
).
La cantidad de información almacenada en pg_statistic
por ANALYZE
, en particular el número máximo de entradas en el most_common_vals
y histogram_bounds
matrices para cada columna, se pueden configurar columna por columna utilizando el ALTER TABLE SET STATISTICS
comando, o globalmente estableciendo la variable de configuración default_statistics_target. El límite predeterminado es actualmente de 100 entradas. El aumento del límite podría permitir que se realicen estimaciones de planificador más precisas, en particular para columnas con distribuciones de datos irregulares, al precio de consumir más espacio en pg_statistic
y un poco más de tiempo para calcular las estimaciones. Por el contrario, un límite inferior podría ser suficiente para columnas con distribuciones de datos simples.
Se pueden encontrar más detalles sobre el uso de las estadísticas por parte del planificador en Capitulo 70.
14.2.2. Estadísticas extendidas
Es común ver consultas lentas que ejecutan planes de ejecución incorrectos porque varias columnas utilizadas en las cláusulas de consulta están correlacionadas. El planificador normalmente asume que múltiples condiciones son independientes entre sí, una suposición que no se cumple cuando los valores de las columnas están correlacionados. Las estadísticas regulares, debido a su naturaleza por columna individual, no pueden capturar ningún conocimiento sobre la correlación entre columnas. Sin embargo, PostgreSQL tiene la capacidad de calcular estadística multivariante, que puede capturar dicha información.
Debido a que el número de posibles combinaciones de columnas es muy grande, no es práctico calcular estadísticas multivariadas automáticamente. En lugar de, objetos de estadísticas extendidas, más a menudo llamado simplemente objetos estadísticos, se puede crear para indicar al servidor que obtenga estadísticas en conjuntos de columnas interesantes.
Los objetos de estadísticas se crean utilizando el comando CREAR ESTADÍSTICAS. La creación de tal objeto simplemente crea una entrada de catálogo que expresa interés en las estadísticas. La recopilación de datos real la realiza ANALYZE
(ya sea un comando manual o un análisis automático en segundo plano). Los valores recopilados se pueden examinar en el pg_statistic_ext_data
catalogar.
ANALYZE
calcula estadísticas extendidas basadas en la misma muestra de filas de la tabla que toma para calcular las estadísticas regulares de una sola columna. Dado que el tamaño de la muestra aumenta al aumentar el objetivo de estadísticas para la tabla o cualquiera de sus columnas (como se describe en la sección anterior), un objetivo de estadísticas más grande normalmente dará como resultado estadísticas extendidas más precisas, así como más tiempo para calcularlas.
Las siguientes subsecciones describen los tipos de estadísticas extendidas que se admiten actualmente.
14.2.2.1. Dependencias funcionales
El tipo más simple de pistas de estadísticas extendidas dependencias funcionales, un concepto utilizado en las definiciones de las formas normales de bases de datos. Decimos esa columna b
es funcionalmente dependiente de la columna a
si el conocimiento del valor de a
es suficiente para determinar el valor de b
, es decir, no hay dos filas que tengan el mismo valor de a
pero diferentes valores de b
. En una base de datos completamente normalizada, las dependencias funcionales deben existir solo en claves primarias y superclaves. Sin embargo, en la práctica, muchos conjuntos de datos no están completamente normalizados por varias razones; La desnormalización intencional por motivos de rendimiento es un ejemplo común. Incluso en una base de datos completamente normalizada, puede haber una correlación parcial entre algunas columnas, que se puede expresar como dependencia funcional parcial.
La existencia de dependencias funcionales afecta directamente la precisión de las estimaciones en determinadas consultas. Si una consulta contiene condiciones en las columnas independientes y dependientes, las condiciones en las columnas dependientes no reducen más el tamaño del resultado; pero sin el conocimiento de la dependencia funcional, el planificador de consultas asumirá que las condiciones son independientes, resultando en subestimar el tamaño del resultado.
Para informar al planificador sobre las dependencias funcionales, ANALYZE
puede recopilar medidas de dependencia entre columnas. Evaluar el grado de dependencia entre todos los conjuntos de columnas sería prohibitivamente costoso, por lo que la recopilación de datos se limita a los grupos de columnas que aparecen juntas en un objeto estadístico definido con el dependencies
opción. Es recomendable crear dependencies
estadísticas solo para grupos de columnas que están fuertemente correlacionados, para evitar gastos generales innecesarios en ambos ANALYZE
y posterior planificación de consultas.
A continuación, se muestra un ejemplo de recopilación de estadísticas de dependencia funcional:
CREATE STATISTICS stts (dependencies) ON city, zip FROM zipcodes; ANALYZE zipcodes; SELECT stxname, stxkeys, stxddependencies FROM pg_statistic_ext join pg_statistic_ext_data on (oid = stxoid) WHERE stxname = 'stts'; stxname | stxkeys | stxddependencies ---------+---------+------------------------------------------ stts | 1 5 | {"1 => 5": 1.000000, "5 => 1": 0.423130} (1 row)
Aquí se puede ver que la columna 1 (código postal) determina completamente la columna 5 (ciudad) por lo que el coeficiente es 1.0, mientras que la ciudad solo determina el código postal alrededor del 42% del tiempo, lo que significa que hay muchas ciudades (58%) que son representado por más de un código postal.
Al calcular la selectividad para una consulta que involucra columnas funcionalmente dependientes, el planificador ajusta las estimaciones de selectividad por condición utilizando los coeficientes de dependencia para no producir una subestimación.
14.2.2.1.1. Limitaciones de las dependencias funcionales
Actualmente, las dependencias funcionales solo se aplican cuando se consideran condiciones de igualdad simples que comparan columnas con valores constantes, y IN
cláusulas con valores constantes. No se utilizan para mejorar las estimaciones de condiciones de igualdad comparando dos columnas o comparando una columna con una expresión, ni para cláusulas de rango, LIKE
o cualquier otro tipo de condición.
Al estimar con dependencias funcionales, el planificador asume que las condiciones en las columnas involucradas son compatibles y, por lo tanto, redundantes. Si son incompatibles, la estimación correcta sería cero filas, pero esa posibilidad no se considera. Por ejemplo, dada una consulta como
SELECT * FROM zipcodes WHERE city = 'San Francisco' AND zip = '94105';
el planificador ignorará el city
cláusula como no cambiar la selectividad, lo cual es correcto. Sin embargo, hará la misma suposición sobre
SELECT * FROM zipcodes WHERE city = 'San Francisco' AND zip = '90210';
aunque realmente no habrá filas que satisfagan esta consulta. Sin embargo, las estadísticas de dependencia funcional no proporcionan suficiente información para concluir eso.
En muchas situaciones prácticas, esta suposición suele satisfacerse; por ejemplo, puede haber una GUI en la aplicación que solo permite seleccionar valores de ciudad y código postal compatibles para usar en una consulta. Pero si ese no es el caso, las dependencias funcionales pueden no ser una opción viable.
14.2.2.2. Recuentos N-Distintos multivariados
Las estadísticas de una sola columna almacenan el número de valores distintos en cada columna. Estimaciones del número de valores distintos al combinar más de una columna (por ejemplo, para GROUP BY a, b
) se equivocan con frecuencia cuando el planificador solo tiene datos estadísticos de una sola columna, lo que hace que seleccione planes incorrectos.
Para mejorar tales estimaciones, ANALYZE
puede recopilar n estadísticas distintas para grupos de columnas. Como antes, no es práctico hacer esto para cada posible agrupación de columnas, por lo que los datos se recopilan solo para aquellos grupos de columnas que aparecen juntas en un objeto de estadísticas definido con el ndistinct
opción. Se recopilarán datos para cada posible combinación de dos o más columnas del conjunto de columnas enumeradas.
Continuando con el ejemplo anterior, los recuentos n distintos en una tabla de códigos postales podrían tener el siguiente aspecto:
CREATE STATISTICS stts2 (ndistinct) ON city, state, zip FROM zipcodes; ANALYZE zipcodes; SELECT stxkeys AS k, stxdndistinct AS nd FROM pg_statistic_ext join pg_statistic_ext_data on (oid = stxoid) WHERE stxname = 'stts2'; -[ RECORD 1 ]-------------------------------------------------------- k | 1 2 5 nd | {"1, 2": 33178, "1, 5": 33178, "2, 5": 27435, "1, 2, 5": 33178} (1 row)
Esto indica que hay tres combinaciones de columnas que tienen 33178 valores distintos: código postal y estado; Código postal y ciudad; y código postal, ciudad y estado (se espera que todos sean iguales dado que el código postal solo es único en esta tabla). Por otro lado, la combinación de ciudad y estado tiene solo 27435 valores distintos.
Es recomendable crear ndistinct
Estadísticas de objetos solo en combinaciones de columnas que se utilizan realmente para la agrupación y para las que una estimación errónea del número de grupos genera malos planes. De lo contrario, el ANALYZE
los ciclos simplemente se desperdician.
14.2.2.3. Listas de MCV multivariante
Otro tipo de estadísticas almacenadas para cada columna son las listas de valores más comunes. Esto permite estimaciones muy precisas para columnas individuales, pero puede resultar en estimaciones erróneas significativas para consultas con condiciones en varias columnas.
Para mejorar tales estimaciones, ANALYZE
puede recopilar listas de MCV en combinaciones de columnas. De manera similar a las dependencias funcionales y los coeficientes n distintos, no es práctico hacer esto para cada posible agrupación de columnas. Más aún en este caso, ya que la lista MCV (a diferencia de las dependencias funcionales y los coeficientes n distintos) almacena los valores de columna comunes. Por lo tanto, los datos se recopilan solo para aquellos grupos de columnas que aparecen juntas en un objeto de estadísticas definido con el mcv
opción.
Continuo En el ejemplo anterior, la lista de MCV para una tabla de códigos postales podría tener el siguiente aspecto (a diferencia de los tipos de estadísticas más simples, se requiere una función para la inspección del contenido de MCV):
CREATE STATISTICS stts3 (mcv) ON city, state FROM zipcodes; ANALYZE zipcodes; SELECT m.* FROM pg_statistic_ext join pg_statistic_ext_data on (oid = stxoid), pg_mcv_list_items(stxdmcv) m WHERE stxname = 'stts3'; index | values | nulls | frequency | base_frequency -------+------------------------+-------+-----------+---------------- 0 | {Washington, DC} | {f,f} | 0.003467 | 2.7e-05 1 | {Apo, AE} | {f,f} | 0.003067 | 1.9e-05 2 | {Houston, TX} | {f,f} | 0.002167 | 0.000133 3 | {El Paso, TX} | {f,f} | 0.002 | 0.000113 4 | {New York, NY} | {f,f} | 0.001967 | 0.000114 5 | {Atlanta, GA} | {f,f} | 0.001633 | 3.3e-05 6 | {Sacramento, CA} | {f,f} | 0.001433 | 7.8e-05 7 | {Miami, FL} | {f,f} | 0.0014 | 6e-05 8 | {Dallas, TX} | {f,f} | 0.001367 | 8.8e-05 9 | {Chicago, IL} | {f,f} | 0.001333 | 5.1e-05 ... (99 rows)
Esto indica que la combinación más común de ciudad y estado es Washington en DC, con una frecuencia real (en la muestra) de aproximadamente 0.35%. La frecuencia base de la combinación (calculada a partir de las frecuencias simples por columna) es solo del 0,0027%, lo que resulta en subestimaciones de dos órdenes de magnitud.
Es aconsejable crear objetos de estadísticas MCV solo en combinaciones de columnas que realmente se usan juntas en condiciones, y para las cuales una estimación errónea del número de grupos da como resultado planes incorrectos. De lo contrario, el ANALYZE
y los ciclos de planificación simplemente se desperdician.
Anterior |
Hasta | próximo |
14.1. Utilizando EXPLAIN |
Hogar | 14.3. Controlar el planificador con explícito JOIN Cláusulas |