Tenho uma tabela que possui mais de 10.000.000 registros, e estou criando uma consulta que retorna cerca de 4436 registros.
Acontece que me dá a impressão de que o custo da consulta para chegar ao último registro é muito alto.
Index Scan using idx_name on task (cost=0.28..142102.57 rows=3470 width=34) (actual time=14.690..22.894 rows=4436 loops=1)
" Index Cond: ((situation = ANY ('{0,1,2,3,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}'::integer[])) AND (deadline < CURRENT_TIMESTAMP))"
Planning Time: 1.335 ms
JIT:
Functions: 5
Options: Inlining false, Optimization false, Expressions true, Deforming true
Timing: Generation 1.654 ms, Inlining 0.000 ms, Optimization 1.214 ms, Emission 13.163 ms, Total 16.030 ms
Execution Time: 24.758 ms
Esse nível de custo é aceitável ou esse índice precisa ser melhorado?
Índice:
CREATE INDEX idx_name ON task (situation, deadline, approved)
WHERE
deadline IS NOT NULL AND
situation <> ALL ('{4,5}'::integer[]) AND
approved = 'N';
Minha consulta:
SELECT
task.deadline,
task.id
FROM
task
WHERE
task.deadline IS NOT NULL
AND task.situation IN ('0', '1', '2', '3', '6' ,'7' ,'8','9','10','11','12','13','14','15','16','17','18','19','20')
AND task.situation NOT IN ('4', '5')
AND task.deadline < CURRENT_TIMESTAMP
AND task.approved = 'N';
Conforme sugerido em um comentário, você não deve observar o custo da consulta, mas sim o tempo de execução real (ou você não deve se incomodar com nada disso se o tempo de execução for aceitável). O custo estimado do plano não é uma indicação de nada, exceto a quantidade relativa de vários recursos que o Postgres estima que pode precisar gastar para executar esse plano específico em comparação com outros planos possíveis .
Observar o valor de custo absoluto não diz absolutamente nada; compará-lo com os custos de outros planos informa qual deles o Postgres considera mais eficiente, com base nas informações que o otimizador possui.
Veja também este Q&A .
Olhando para a boca do cavalo, pode-se ver isso:
e mais adiante nesse caminho:
Você notará que eles enfatizam "unidades arbitrárias" em uma "escala arbitrária"; tudo o que eles querem estabelecer é que ler N páginas aleatoriamente é quatro vezes mais intensivo em recursos ("caro") do que ler tantas páginas sequencialmente, ou que avaliar um predicado em relação a uma entrada de índice é metade do custo de fazer isso em uma tabela fileira. Quando o custo de toda a árvore do plano é somado, obtém-se um valor que só pode ser comparado ao custo de outra árvore; só pode ser mais alto ou mais baixo , não alto ou baixo.
HT para jjanes por mencionar JIT em um comentário. O custo estimado da consulta no seu caso acaba excedendo o limite do gatilho JIT, que é padrão em 100.000 e, como jjanes apontou astutamente, "2/3 do tempo [de sua consulta] parece ser gasto em JIT", o que provavelmente é contraproducente. Você pode querer avaliar se ter o JIT ativado em seu ambiente é útil.
O índice é bom, a consulta é rápida.
Mas o índice pode ser melhor e a consulta mais rápida. A coluna de índice
approved
é apenas frete morto com a condiçãoapproved = 'N'
. Remova.Importa para o tamanho do índice, mesmo que
approved
sejavarchar(1)
(e provavelmente deveria serboolean
). Comodeadline
é um tipo (alinhado)timestamp
(with time zone
), a coluna de índice adicionadaapproved
desperdiça pelo menos 8 bytes por tupla de índice - faz com que ela cresça em 1/3.Melhor ainda, se o exemplo com
SELECT deadline, id ...
for qualquer indicação, vale a pena anexarid
como coluna "incluída" para permitir varreduras somente de índice :Requer Postgres 11 ou posterior. Traz o tamanho de volta ao que era antes.
Ver:
Ambas as outras respostas são muito boas, deixe-me adicionar um detalhe:
Ambas as suas condições não são comparações de igualdade simples, portanto, a segunda coluna no índice será usada apenas para filtrar linhas (antes que a tabela seja acessada). Portanto, a ordem das colunas de índice influenciará quantas entradas de índice devem ser lidas para encontrar a resposta.
Para encontrar o melhor índice para a consulta, primeiro tente com um índice em
(situation, deadline)
, depois elimine esse índice e tente com um em(deadline, situation)
, e veja qual deles toca menos blocos comEXPLAIN (ANALYZE, BUFFERS)
.