Eu tenho uma tabela do PostgreSQL 9.3 com alguns números e alguns dados adicionais:
CREATE TABLE mytable (
myid BIGINT,
somedata BYTEA
)
Atualmente, esta tabela possui cerca de 10 milhões de registros e ocupa 1 GB de espaço em disco. myid
não são consecutivos.
Quero calcular quantas linhas existem em cada bloco de 100000 números consecutivos:
SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;
Isso retorna cerca de 3500 linhas.
Percebi que a existência de um determinado índice acelera significativamente essa consulta, embora o plano de consulta não a mencione. O plano de consulta sem o í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)
O índice:
db=> CREATE INDEX myindex ON mytable ((myid/100000));
db=> VACUUM ANALYZE;
O novo plano 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)
Portanto, os planos de consulta e os tempos de execução diferem significativamente (quase três vezes), mas nenhum deles menciona o índice. Esse comportamento é perfeitamente reproduzível na minha máquina de desenvolvimento: passei por vários ciclos de eliminação do índice, testando a consulta várias vezes, recriando o índice, testando novamente a consulta várias vezes. O que está acontecendo aqui?
HashAggregate
método (e nenhuma classificação é necessária), para que você obtenha melhor desempenho. Por que o índice não é mencionado no plano, não faço ideia.explain (analyze true, verbose true) ...
:?Respostas:
VACUUM ANALYZE
faz a diferença no seu exemplo. Além disso, como o @jjanes forneceu , as estatísticas adicionais para o índice funcional. Por documentação:No entanto, a criação do índice não faz com que o Postgres colete estatísticas. Tentar:
Não retorna nada até você executar seu primeiro
ANALYZE
(ouVACUUM ANALYZE
, ou o daemon de autovacuum entra em ação).Agora você verá estatísticas adicionadas.
Como a tabela inteira precisa ser lida de qualquer maneira, o Postgres usará uma varredura seqüencial, a menos que espere que o cálculo
myid/100000
seja caro o suficiente para alternar, o que não é.Sua única outra chance seria uma verificação somente de índice se o índice for muito menor que a tabela - e as condições prévias para uma verificação somente de índice forem atendidas. Detalhes no Wiki do Postgres e no manual .
Enquanto esse índice funcional não for usado, o benefício colateral das estatísticas adicionadas é moderado. Se a tabela fosse somente leitura, o custo seria baixo - mas, novamente, provavelmente veríamos uma verificação somente de índice imediatamente.
Talvez você também possa obter melhores planos de consulta definindo um destino de estatísticas mais alto para
mytable.myid
. Isso custaria apenas um custo menor. Mais:fonte
myid/100000 BETWEEN somevalue AND othervalue
condição adicional , portanto o índice será usado no plano de consulta de qualquer maneira - acabei de fazer essa pergunta porque não entendi por que o índice é útil no caso de toda a tabela.WHERE myid BETWEEN somevalue*100000 AND othervalue*100000
(considere os efeitos de arredondamento, dependendo do seu tipo), e provavelmente já possui um índice simplesmyid
, para que possa ficar sem um índice especializado adicional. Pode ser mais eficiente.Quando você cria um índice de expressão, ele faz com que o PostgreSQL colete estatísticas sobre essa expressão. Com essas estatísticas em mãos, agora ela tem uma estimativa precisa do número de linhas agregadas que a consulta retornará, o que a leva a fazer uma melhor escolha de plano.
Especificamente nesse caso, sem essas estatísticas extras, a tabela de hash seria grande demais para caber no work_mem, portanto, não foi escolhido esse método.
fonte
work_mem
em consideração o valor . Se você o criou para que o tipo caiba na memória, ainda assim usaria o mesmo plano. Deixe-me observar aqui que a diferença de horário (a maior parte) vem da classificação do disco externo.