Tengo una tabla PostgreSQL 9.3 con algunos números y algunos datos adicionales:
CREATE TABLE mytable (
myid BIGINT,
somedata BYTEA
)
Actualmente, esta tabla tiene aproximadamente 10 millones de registros y ocupa 1 GB de espacio en disco. myid
No son consecutivos.
Quiero calcular cuántas filas hay en cada bloque de 100000 números consecutivos:
SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;
Esto devuelve alrededor de 3500 filas.
Noté que la existencia de un cierto índice acelera significativamente esta consulta a pesar de que el plan de consulta no lo menciona en absoluto. El plan de consulta sin el índice:
db=> EXPLAIN (ANALYZE TRUE, VERBOSE TRUE) SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------
GroupAggregate (cost=1636639.92..1709958.65 rows=496942 width=8) (actual time=6783.763..8888.841 rows=3460 loops=1)
Output: ((myid / 100000)), count(*)
-> Sort (cost=1636639.92..1659008.91 rows=8947594 width=8) (actual time=6783.752..8005.831 rows=8947557 loops=1)
Output: ((myid / 100000))
Sort Key: ((mytable.myid / 100000))
Sort Method: external merge Disk: 157440kB
-> Seq Scan on public.mytable (cost=0.00..236506.92 rows=8947594 width=8) (actual time=0.020..1674.838 rows=8947557 loops=1)
Output: (myid / 100000)
Total runtime: 8914.780 ms
(9 rows)
El índice:
db=> CREATE INDEX myindex ON mytable ((myid/100000));
db=> VACUUM ANALYZE;
El nuevo plan de consulta:
db=> EXPLAIN (ANALYZE TRUE, VERBOSE TRUE) SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------
HashAggregate (cost=281242.99..281285.97 rows=3439 width=8) (actual time=3190.189..3190.800 rows=3460 loops=1)
Output: ((myid / 100000)), count(*)
-> Seq Scan on public.mytable (cost=0.00..236505.56 rows=8947485 width=8) (actual time=0.026..1659.571 rows=8947557 loops=1)
Output: (myid / 100000)
Total runtime: 3190.975 ms
(5 rows)
Entonces, los planes de consulta y los tiempos de ejecución difieren significativamente (casi tres veces) pero ninguno menciona el índice. Este comportamiento es perfectamente reproducible en mi máquina de desarrollo: pasé por varios ciclos de caída del índice, probando la consulta varias veces, recreando el índice, nuevamente probando la consulta varias veces. ¿Que esta pasando aqui?
HashAggregate
método (y no se requiere clasificación), por lo que obtienes un mejor rendimiento. Por qué el índice no se menciona en el plan, no tengo ni idea.explain (analyze true, verbose true) ...
:?Respuestas:
VACUUM ANALYZE
hace la diferencia en tu ejemplo. Además, como proporcionó @jjanes , las estadísticas adicionales para el índice funcional. Por documentación:Sin embargo, crear el índice por sí solo no hace que Postgres recopile estadísticas. Tratar:
No devuelve nada hasta que ejecuta su primer
ANALYZE
(oVACUUM ANALYZE
, o el demonio autovacuum entra en acción).Ahora verás estadísticas adicionales.
Dado que la tabla completa tiene que leerse de todos modos, Postgres usará un escaneo secuencial a menos que espere que el cálculo
myid/100000
sea lo suficientemente costoso como para cambiar, lo cual no es así.Su única otra posibilidad sería una exploración de solo índice si el índice es mucho más pequeño que la tabla, y se cumplen las condiciones previas para una exploración de solo índice. Detalles en el Wiki de Postgres y en el manual .
Mientras no se use ese índice funcional, el beneficio colateral de las estadísticas agregadas es moderado. Si la tabla fuera de solo lectura, el costo sería bajo, pero nuevamente, probablemente veríamos un escaneo de solo índice de inmediato.
Tal vez también pueda lograr mejores planes de consulta estableciendo un objetivo de estadísticas más alto para
mytable.myid
. Eso solo incurriría en un costo menor. Más:fuente
myid/100000 BETWEEN somevalue AND othervalue
condición adicional , de modo que el índice se utilizará en el plan de consulta de todos modos: acabo de hacer esta pregunta porque no entendí por qué el índice es útil en el caso de la tabla completa.WHERE myid BETWEEN somevalue*100000 AND othervalue*100000
(considere los efectos de redondeo según sus tipos), y probablemente ya tenga un índice simplemyid
, por lo que puede prescindir de un índice especializado adicional. Podría ser más eficiente.Cuando crea un índice de expresión, hace que PostgreSQL recopile estadísticas sobre esa expresión. Con esas estadísticas en la mano, ahora tiene una estimación precisa del número de filas agregadas que devolverá la consulta, lo que la lleva a hacer una mejor elección de plan.
Específicamente en este caso, sin esas estadísticas adicionales, pensó que la tabla hash sería demasiado grande para caber en work_mem, por lo que no eligió ese método.
fuente
work_mem
en cuenta el valor de . Si lo planteó para que el tipo se ajuste a la memoria, aún usaría el mismo plan. Permítanme señalar aquí que la diferencia horaria (la mayor parte) proviene del tipo de disco externo.