Estos son los resultados de consultar una tabla en la segunda columna de un índice de varias columnas .
Los efectos son fáciles de reproducir para cualquiera. Solo inténtalo en casa.
Probé con PostgreSQL 9.0.5 en Debian usando una tabla de tamaño medio de una base de datos de la vida real con 23322 filas. Implementa la relación n: m entre las tablas adr
(dirección) y att
(atributo), pero eso no es relevante aquí. Esquema simplificado:
CREATE TABLE adratt (
adratt_id serial PRIMARY KEY
, adr_id integer NOT NULL
, att_id integer NOT NULL
, log_up timestamp(0) NOT NULL DEFAULT (now())::timestamp(0)
, CONSTRAINT adratt_uni UNIQUE (adr_id, att_id)
);
La UNIQUE
restricción implementa efectivamente un índice único. Repetí la prueba con un índice simple para estar seguro y obtuve resultados idénticos a los esperados.
CREATE INDEX adratt_idx ON adratt(adr_id, att_id)
La tabla está agrupada en el adratt_uni
índice y antes de la prueba que ejecuté:
CLUSTER adratt;
ANALYZE adratt;
Los escaneos secuenciales para consultas (adr_id, att_id)
son tan rápidos como sea posible. El índice de varias columnas se seguirá utilizando para una condición de consulta solo en la segunda columna de índice.
Ejecuté las consultas un par de veces para llenar el caché y elegí la mejor de diez ejecuciones para obtener resultados comparables.
1. Consulta usando ambas columnas
SELECT *
FROM adratt
WHERE att_id = 90
AND adr_id = 10;
adratt_id | adr_id | att_id | log_up
-----------+--------+--------+---------------------
123 | 10 | 90 | 2008-07-29 09:35:54
(1 row)
Salida de EXPLAIN ANALYZE
:
Index Scan using adratt_uni on adratt (cost=0.00..3.48 rows=1 width=20) (actual time=0.022..0.025 rows=1 loops=1)
Index Cond: ((adr_id = 10) AND (att_id = 90))
Total runtime: 0.067 ms
2. Consulta usando la primera columna
SELECT * FROM adratt WHERE adr_id = 10
adratt_id | adr_id | att_id | log_up
-----------+--------+--------+---------------------
126 | 10 | 10 | 2008-07-29 09:35:54
125 | 10 | 13 | 2008-07-29 09:35:54
4711 | 10 | 21 | 2008-07-29 09:35:54
29322 | 10 | 22 | 2011-06-06 15:50:38
29321 | 10 | 30 | 2011-06-06 15:47:17
124 | 10 | 62 | 2008-07-29 09:35:54
21913 | 10 | 78 | 2008-07-29 09:35:54
123 | 10 | 90 | 2008-07-29 09:35:54
28352 | 10 | 106 | 2010-11-22 12:37:50
(9 rows)
Salida de EXPLAIN ANALYZE
:
Index Scan using adratt_uni on adratt (cost=0.00..8.23 rows=9 width=20) (actual time=0.007..0.023 rows=9 loops=1)
Index Cond: (adr_id = 10)
Total runtime: 0.058 ms
3. Consulta usando la segunda columna
SELECT * FROM adratt WHERE att_id = 90
adratt_id | adr_id | att_id | log_up
-----------+--------+--------+---------------------
123 | 10 | 90 | 2008-07-29 09:35:54
180 | 39 | 90 | 2008-08-29 15:46:07
...
(83 rows)
Salida de EXPLAIN ANALYZE
:
Index Scan using adratt_uni on adratt (cost=0.00..818.51 rows=83 width=20) (actual time=0.014..0.694 rows=83 loops=1)
Index Cond: (att_id = 90)
Total runtime: 0.849 ms
4. Deshabilitar indexscan y bitmapscan
SET enable_indexscan = off;
SELECT * FROM adratt WHERE att_id = 90
Salida de EXPLAIN ANALYZE:
Bitmap Heap Scan on adratt (cost=779.94..854.74 rows=83 width=20) (actual time=0.558..0.743 rows=83 loops=1)
Recheck Cond: (att_id = 90)
-> Bitmap Index Scan on adratt_uni (cost=0.00..779.86 rows=83 width=0) (actual time=0.544..0.544 rows=83 loops=1)
Index Cond: (att_id = 90)
Total runtime: 0.894 ms
SET enable_bitmapscan = off
SELECT * FROM adratt WHERE att_id = 90
Salida de EXPLAIN ANALYZE
:
Seq Scan on adratt (cost=0.00..1323.10 rows=83 width=20) (actual time=0.009..2.429 rows=83 loops=1)
Filter: (att_id = 90)
Total runtime: 2.680 ms
Conclusión
Como se esperaba, el índice de varias columnas se usa para una consulta solo en la segunda columna.
Como se esperaba, es menos efectivo, pero la consulta sigue siendo 3 veces más rápida que sin el índice.
Después de deshabilitar las exploraciones de índice, el planificador de consultas elige una exploración de montón de mapa de bits, que funciona casi tan rápido. Solo después de deshabilitar eso, también, recurre a un escaneo secuencial.
vacuum full
y areindex
. Aparte de eso, ayudará a indexar los escaneos en la primera o ambas columnas iniciales mucho , pero perjudicará las consultas en la segunda columna. En una tabla recién agrupada, las filas con el mismo valor en la segunda columna se extienden, por lo que deberá leerse un máximo de bloques.re 1) Sí y no.
Para una consulta que usa ambas columnas, por ejemplo
where (user_id1, user_id2) = (1,2)
, no importa qué índice se cree.Para una consulta que tiene una condición en solo una de las columnas, por ejemplo
where user_id1 = 1
, es importante porque generalmente el optimizador solo puede usar las columnas "iniciales" para una comparación. Porwhere user_id1 = 1
lo tanto, podría usar el índice (user_id1, user_id2) pero no podría usar un índice (user_id2, user_id1) para todos los casos.Después de jugar con esto (después de que Erwin nos mostró amablemente una configuración donde funciona), parece que esto depende en gran medida de la distribución de datos de la segunda columna, aunque todavía no he descubierto qué situación permite que el optimizador use columnas finales para una condición WHERE.
Oracle 11 que también puede (a veces) usar columnas que no están al comienzo de la definición del índice.
re 2) Sí, creará un índice
Cita del manual
re 2a)
Primary Key (user_id1,user_id2)
creará un índice en (user_id1, user_id2) (que puede descubrir usted mismo muy fácilmente simplemente creando una clave primaria de este tipo)Le recomiendo que lea el capítulo sobre índices en el manual , básicamente responde a todas las preguntas anteriores.
Además, ¿qué índice crear? by depesz hace un buen trabajo al explicar el orden en las columnas de índice y otros temas relacionados con el índice.
fuente
Anuncio 1)
Existen limitaciones en PostgreSQL como @a_horse_with_no_name describe . Hasta la versión 8.0, los índices de varias columnas solo se podían usar para consultas en las columnas iniciales. Esto se ha mejorado en la versión 8.1. El manual actual para Postgres 10 (actualizado) explica:
El énfasis es mío. Puedo confirmar eso por experiencia.
También vea el caso de prueba agregado mi respuesta posterior aquí .
fuente
Esto es en respuesta a la respuesta de Jack , un comentario no serviría.
No había índices de cobertura en PostgreSQL antes de la versión 9.2. Debido al modelo MVCC, se debe visitar cada tupla en el conjunto de resultados para verificar la visibilidad. Puede que estés pensando en Oracle.
Los desarrolladores de PostgreSQL hablan sobre "escaneos de solo índice" . De hecho, la función se ha lanzado con Postgres 9.2. Lee el mensaje de confirmación .
Depesz escribió una publicación de blog muy informativa .
Los índices de cobertura verdaderos (actualización) se presentan con la
INCLUDE
cláusula con Postgres 11. Relacionado:Esto también es un poco extraño:
Como se informó en los comentarios sobre mi otra respuesta, también he realizado pruebas con una tabla de dos enteros y nada más. El índice contiene las mismas columnas que la tabla. El tamaño de un índice btree es aproximadamente 2/3 del de la tabla. No es suficiente para explicar una aceleración del factor 3. Ejecuté más pruebas, basadas en su configuración, simplificadas a dos columnas y con 100000 filas. En mi instalación de PostgreSQL 9.0, los resultados fueron consistentes.
Si la tabla tiene columnas adicionales, la aceleración con índice se vuelve más sustancial, pero ese no es el único factor aquí .
Para resumir los puntos principales:
Los índices de varias columnas se pueden usar con consultas en columnas no iniciales, pero la aceleración es solo alrededor del factor 3 para criterios selectivos (pequeño porcentaje de filas en el resultado). Más alto para tuplas más grandes, más bajo para porciones más grandes de la tabla en el conjunto de resultados.
Cree un índice adicional en esas columnas si el rendimiento es importante.
Si todas las columnas involucradas están incluidas en un índice (índice de cobertura) y todas las filas involucradas (por bloque) son visibles para todas las transacciones, puede obtener un "escaneo de solo índice" en la página 9.2 o posterior.
fuente
Estos no son equivalentes y, en general, el índice (bar, baz) no será eficiente para consultas del formulario
select * from foo where baz=?
Erwin ha demostrado que tales índices pueden acelerar una consulta, pero este efecto es limitado y no es del mismo orden que generalmente se espera que un índice mejore una búsqueda: se basa en el hecho de que a menudo se realiza una 'exploración completa' de un índice más rápido que una 'exploración completa' de la tabla indexada debido a las columnas adicionales en la tabla que no aparecen en el índice.
Resumen: los índices pueden ayudar a las consultas incluso en columnas no iniciales, pero de una de las dos formas secundarias y relativamente menores y no de la forma dramática que normalmente espera que un índice ayude debido a su estructura btree
nb las dos formas en que el índice puede ayudar son si un análisis completo del índice es significativamente más barato que un análisis completo de la tabla y: 1. las búsquedas de la tabla son baratas (porque hay pocas o están agrupadas),
o 2. el índice está cubriendo, por lo que no hay búsquedas de tablas en absoluto, vea los comentarios de Erwins aquíbanco de pruebas:
consulta 1 (sin índice, golpeando 74 buffers ):
consulta 2 (con índice - el optimizador ignora el índice - golpeando 74 buffers nuevamente):
consulta 2 (con índice, y engañamos al optimizador para que lo use):
Por lo tanto, el acceso a través del índice es el doble de rápido en este caso, llegando a 30 buffers , ¡lo que en términos de indexación es 'ligeramente más rápido'! de los datos en la tabla
Por el contrario, las consultas en la columna inicial hacen uso de la estructura btree del índice, en este caso golpeando 2 buffers :
fuente