Índice de varias columnas y rendimiento

31

Tengo una tabla con un índice de varias columnas, y tengo dudas sobre la clasificación adecuada de los índices para obtener el máximo rendimiento en las consultas.

El escenario:

  • PostgreSQL 8.4, tabla con aproximadamente un millón de filas

  • Los valores en la columna c1 pueden tener aproximadamente 100 valores diferentes . Podemos suponer que los valores están distribuidos uniformemente, por lo que tenemos alrededor de 10000 filas para cada valor posible.

  • La columna c2 puede tener 1000 valores diferentes . Tenemos 1000 filas para cada valor posible.

Al buscar datos, la condición siempre incluye valores para estas dos columnas, por lo que la tabla tiene un índice de varias columnas que combina c1 y c2. He leído sobre la importancia de ordenar correctamente las columnas en un índice de varias columnas si tiene consultas utilizando solo una columna para el filtrado. Este no es el caso en nuestro escenario.

Mi pregunta es esta:

Dado el hecho de que uno de los filtros selecciona un conjunto de datos mucho más pequeño, ¿podría mejorar el rendimiento si el primer índice es el más selectivo (el que permite un conjunto más pequeño)? Nunca había considerado esta pregunta hasta que vi los gráficos del artículo referenciado:

ingrese la descripción de la imagen aquí

Imagen tomada del artículo referenciado sobre índices de varias columnas .

Las consultas usan valores de las dos columnas para el filtrado. No tengo consultas usando solo una columna para filtrar. Todos ellos son: WHERE c1=@ParameterA AND c2=@ParameterB. También hay condiciones como esta:WHERE c1 = "abc" AND c2 LIKE "ab%"

jap1968
fuente

Respuestas:

36

Responder

Como se refiere al sitio web use-the-index-luke.com, considere el capítulo:

Use The Index, Luke ›La cláusula Where› Buscar rangos › Mayor, menor y ENTRE

Tiene un ejemplo que coincide perfectamente con su situación (índice de dos columnas, uno se prueba para la igualdad , el otro para el rango ), explica (con más de esos buenos gráficos de índice) por qué el consejo de @ ypercube es preciso y lo resume:

Rule of thumb: index for equality first  then for ranges.

¿También es bueno para una sola columna?

Parece claro qué hacer para las consultas en una sola columna . Más detalles y puntos de referencia con respecto a eso bajo estas preguntas relacionadas:

¿Columna menos selectiva primero?

Aparte de eso, ¿qué pasa si solo tiene condiciones de igualdad para ambas columnas ?

No importa . Ponga la columna primero que es más probable que reciba condiciones propias, lo que realmente importa.

Considere esta demostración o reprodúzcala usted mismo. Creo una tabla simple de dos columnas con 100k filas. Uno con muy pocos , el otro con muchos valores distintos:

CREATE TEMP TABLE t AS
SELECT (random() * 10000)::int AS lots
     , (random() * 4)::int     AS few
FROM generate_series (1, 100000);

DELETE FROM t WHERE random() > 0.9;  -- create some dead tuples, more "real-life"

ANALYZE t;

SELECT count(distinct lots)   -- 9999
     , count(distinct few)    --    5
FROM   t;

Consulta:

SELECT *
FROM   t
WHERE  lots = 2345
AND    few = 2;

EXPLAIN ANALYZE salida (Lo mejor de 10 para excluir los efectos de almacenamiento en caché):

Seq Scan en t (costo = 0.00..5840.84 filas = 2 ancho = 8)
               (tiempo real = 5.646..15.535 filas = 2 bucles = 1)
  Filtro: ((lotes = 2345) Y (pocos = 2))
  Buffers: golpe local = 443
Tiempo de ejecución total: 15.557 ms

Añadir índice, volver a probar:

CREATE INDEX t_lf_idx ON t(lots, few);
Escaneo de índice usando t_lf_idx en t (costo = 0.00..3.76 filas = 2 ancho = 8)
                                (tiempo real = 0.008..0.011 filas = 2 bucles = 1)
  Índice Cond: ((lotes = 2345) Y (pocos = 2))
  Buffers: golpe local = 4
Tiempo de ejecución total: 0.027 ms

Agregue otro índice, vuelva a probar:

DROP INDEX t_lf_idx;
CREATE INDEX t_fl_idx  ON t(few, lots);
Escaneo de índice usando t_fl_idx en t (costo = 0.00..3.74 filas = 2 ancho = 8)
                                (tiempo real = 0.007..0.011 filas = 2 bucles = 1)
  Índice Cond: ((pocos = 2) Y (lotes = 2345))
  Buffers: golpe local = 4
Tiempo de ejecución total: 0.027 ms
Erwin Brandstetter
fuente
¿Es este también el caso de 3 (o más) columnas en el índice?
Hayd
@hayd: No estoy seguro de a qué se refiere "esto". Puedes hacer una nueva pregunta . Siempre puede hacer referencia a este para el contexto. (Y deje un comentario aquí para vincular de nuevo.)
Erwin Brandstetter
Con "esto" quiero decir "importa el orden de la definición del índice si hay más de 2 columnas en la definición del índice"
hayd
@hayd: La mayor punto importante: un índice de árbolB es bueno para las consultas con las condiciones de igualdad en las principales expresiones de índice. El orden entre ellos es sobre todo irrelevante. Muchos otros detalles que no caben en un comentario ...
Erwin Brandstetter
Gracias, intentaré escribir una pregunta coherente y vincularla.
hayd
11

Si, como usted dice, las consultas que involucran estas 2 columnas, son todas verificaciones de igualdad de ambas columnas, por ejemplo:

WHERE c1=@ParameterA AND c2=@ParameterB

No te molestes con esto. Dudo que haya alguna diferencia y si la hay, será insignificante. Siempre puede probar, por supuesto, con sus datos y la configuración de su servidor. Las diferentes versiones de un DBMS pueden comportarse de manera ligeramente diferente con respecto a la optimización.

El orden dentro del índice sería importante para otros tipos de consultas, verificaciones de una sola columna, o condiciones de desigualdad, o condiciones en una columna y agrupación en la otra, etc.

Si tuviera que elegir una de las dos órdenes, elegiría poner primero la columna menos selectiva. Considere una tabla con columnas yeary month. Es más probable que necesite una WHERE year = 2000condición o una WHERE year BETWEEN 2000 AND 2013o una WHERE (year, month) BETWEEN (1999, 6) AND (2000, 5).

Una consulta del tipo WHERE month = 7 GROUP BY yearpuede ser segura (Buscar personas nacidas en julio), pero sería menos frecuente. Eso depende, por supuesto, de los datos reales almacenados en su tabla. Elija un pedido por ahora, diga el (c1, c2)y siempre puede agregar otro índice más adelante (c2, c1).


Actualización, después del comentario del OP:

También hay condiciones como esta: WHERE c1 = 'abc' AND c2 LIKE 'ab%'

Este tipo de consulta es exactamente una condición de rango en la c2columna y necesitaría un (c1, c2)índice. Si también tiene consultas del tipo inverso:

WHERE c2 = 'abc' AND c1 LIKE 'ab%'

entonces sería bueno si tuvieras un (c2, c1)índice también.

ypercubeᵀᴹ
fuente