2012-10-03 18 views
5

Tengo la siguiente tabla/índices -Postgres que combinan varios índices

CREATE TABLE test 
(
    coords geography(Point,4326), 
    user_id varchar(50), 
    created_at timestamp 
); 
CREATE INDEX ix_coords ON test USING GIST (coords); 
CREATE INDEX ix_user_id ON test (user_id); 
CREATE INDEX ix_created_at ON test (created_at DESC); 

Ésta es la consulta que desea ejecutar:

select * 
from updates 
where ST_DWithin(coords, ST_MakePoint(-126.4, 45.32)::geography, 30000) 
and user_id='3212312' 
order by created_at desc 
limit 60 

Cuando ejecuto la consulta sólo se utiliza ix_coords índice. ¿Cómo puedo asegurarme de que Postgres usa los índices ix_user_id y ix_created_at para la consulta?

Esta es una nueva tabla en la que hice una inserción masiva de datos de producción. total de filas en la tabla test: 15.069.489

Me postulo PostgreSQL 9.2.1 (con Postgis) con (effective_cache_size = 2 GB). Este es mi OSX local con 16 GB de RAM, Core i7/2.5 GHz, disco que no es SSD.

Adición de la salida de EXPLAIN ANALYZE -

Limit (cost=71.64..71.65 rows=1 width=280) (actual time=1278.652..1278.665 rows=60 loops=1) 
    -> Sort (cost=71.64..71.65 rows=1 width=280) (actual time=1278.651..1278.662 rows=60 loops=1) 
     Sort Key: created_at 
     Sort Method: top-N heapsort Memory: 33kB 
     -> Index Scan using ix_coords on test (cost=0.00..71.63 rows=1 width=280) (actual time=0.198..1278.227 rows=178 loops=1) 
       Index Cond: (coords && '0101000020E61000006666666666E63C40C3F5285C8F824440'::geography) 
       Filter: (((user_id)::text = '4f1092000b921a000100015c'::text) AND ('0101000020E61000006666666666E63C40C3F5285C8F824440'::geography && _st_expand(coords, 30000::double precision)) AND _st_dwithin(coords, '0101000020E61000006666666666E63C40C3F5285C8F824440'::geography, 30000::double precision, true)) 
       Rows Removed by Filter: 3122459 
Total runtime: 1278.701 ms 

ACTUALIZACIÓN:

Sobre la base de las sugerencias a continuación he intentado índice en los cables + user_id:

CREATE INDEX ix_coords_and_user_id ON updates USING GIST (coords, user_id); 

..pero sale el siguiente error:

ERROR: data type character varying has no default operator class for access method "gist" 
HINT: You must specify an operator class for the index or define a default operator class for the data type. 

ACTUALIZACIÓN:

Así que el CREATE EXTENSION btree_gist; resolvió el problema del índice compuesto btree/gist. Y ahora mi índice parece

CREATE INDEX ix_coords_user_id_created_at ON test USING GIST (coords, user_id, created_at); 

NOTA: btree_gist no acepta DESC/ASC.

nuevo plan de consulta:

Limit (cost=134.99..135.00 rows=1 width=280) (actual time=273.282..273.292 rows=60 loops=1) 
    -> Sort (cost=134.99..135.00 rows=1 width=280) (actual time=273.281..273.285 rows=60 loops=1) 
     Sort Key: created_at 
     Sort Method: quicksort Memory: 41kB 
     -> Index Scan using ix_updates_coords_user_id_created_at on updates (cost=0.00..134.98 rows=1 width=280) (actual time=0.406..273.110 rows=115 loops=1) 
       Index Cond: ((coords && '0101000020E61000006666666666E63C40C3F5285C8F824440'::geography) AND ((user_id)::text = '4e952bb5b9a77200010019ad'::text)) 
       Filter: (('0101000020E61000006666666666E63C40C3F5285C8F824440'::geography && _st_expand(coords, 30000::double precision)) AND _st_dwithin(coords, '0101000020E61000006666666666E63C40C3F5285C8F824440'::geography, 30000::double precision, true)) 
       Rows Removed by Filter: 1 
Total runtime: 273.331 ms 

La consulta se está desempeñando mejor que antes, casi un segundo mejor, pero todavía no es muy bueno. ¿Supongo que esto es lo mejor que puedo conseguir? Esperaba algo entre 60 y 80 ms. También tomando order by created_at desc de la consulta, reduce otros 100 ms, lo que significa que no puede usar el índice. ¿Cualquier forma de arreglar esto?

+0

Postgres utiliza un planificador basado en los costos. Incluso si PUEDE usar el índice, puede que no sea tan rápido como no usarlo. Puedes jugar con random_page_cost y cpu * cost vars para ver si puedes hablar usando esos índices. Use explicar analizar para ver qué decide hacer y qué tan rápido es. –

+0

El uso de un índice también depende de las estadísticas disponibles. ¿Cuántas filas realmente tienen 'user_id = '3212312''? ¿Has hecho un 'análisis de vacío' antes de esta consulta (al menos después de completar la tabla)? – wildplasser

+0

Para ver qué hace cuando el índice 'ix_coords' no está disponible, si puede usar el otro índice y cuál es el costo, intente' BEGIN; INDICE DE GOTA ix_coords ON thetable; EXPLICAR ANALIZAR the_query; ROLLBACK; '. –

Respuesta

5

No sé si Pg puede combinar un índice GiST e índices b-tree normales con un análisis de índice de mapa de bits, pero sospecho que no. Puede obtener el mejor resultado posible sin agregar una columna user_id a su índice GiST (y, en consecuencia, hacerlo más grande y más lento para otras consultas que no usan user_id).

Como un experimento que podría:

CREATE EXTENSION btree_gist; 
CREATE INDEX ix_coords_and_user_id ON test USING GIST (coords, user_id); 

que es probable que resulte en un gran índice, pero podría aumentar esa consulta - si funciona. Tenga en cuenta que mantener dicho índice disminuirá significativamente INSERT y UPDATE s. Si abandona el anterior ix_coords sus consultas utilizarán ix_coords_and_user_id incluso si no se filtran en user_id, pero será más lento que ix_coords. Mantener ambos hará que la ralentización de INSERT y UPDATE sea aún peor.

Ver btree-gist


(obsoleto en Editar para cuestionar que cambia por completo la cuestión; cuando se escribe el usuario tenía un índice de múltiples ahora han dividieron en dos separados):

Parece que no está filtrando ni ordenando en user_id, solo create_date. Pg no usará (¿no puede?) Solo el segundo término de un índice de varias columnas como (user_id, create_date), también necesita el uso del primer elemento.

Si desea indexar create_date, cree un índice separado para él. Si usa y necesita el índice (user_id, create_date) y generalmente no usa solo user_id, vea si puede revertir el orden de las columnas. Alternativamente cree dos índices independientes, (user_id) y (create_date). Cuando se necesitan ambas columnas, Pg puede combinar los dos índices independientes utilizando un análisis de índice de mapa de bits.

+0

lo siento, tuve algunos errores ortográficos en la pregunta, tenía una identificación mixta y un id_usuario, básicamente es solo "user_id". – kapso

+0

He agregado el resultado del análisis de explicación. Aprecio tu ayuda. – kapso

+0

@ user310525 Parece que ha cambiado por completo sus definiciones de índice dividiendo el componente 'user_id' de ix_created_at' en un nuevo índice. ¿Estaba el viejo simplemente equivocado? ¿O ha cambiado su configuración y no ha explicado eso? Si lo cambias, es mejor explicar y agregar material nuevo, no solo cambiar silenciosamente lo que hay allí, entonces las respuestas antiguas ya no tienen sentido en contexto. –

2

creo que Craig es correcta con su respuesta, pero yo sólo quería añadir algunas cosas (y no encajaría en un comentario)

Tienes que trabajar muy duro para la fuerza PostgreSQL para usar un índice. El optimizador de consultas es inteligente y hay momentos en los que creerá que un escaneo de tabla secuencial será más rápido. ¡Por lo general es correcto! :) Sin embargo, puedes jugar con algunas configuraciones (como seq_page_cost, random_page_cost, etc.) con las que puedes jugar para intentar favorecer un índice. Aquí hay un enlace a algunos de los configurations que es posible que desee examinar si siente que no está tomando la decisión correcta. Pero, de nuevo ... mi experiencia es que la mayoría de las veces, ¡Postgres es más inteligente que yo! :)

Espero que esto te ayude a ti (oa alguien en el futuro).

Cuestiones relacionadas