Varreduras lentas de índice em uma tabela grande

12

Usando o PostgreSQL 9.2, tenho problemas com consultas lentas em uma tabela relativamente grande (mais de 200 milhões de linhas). Não estou tentando nada louco, apenas adicionando valores históricos. Abaixo está a consulta e a saída do plano de consulta.

Meu layout de tabela:

                                   Table "public.energy_energyentry"
  Column   |           Type           |                            Modifiers
-----------+--------------------------+-----------------------------------------------------------------
 id        | integer                  | not null default nextval('energy_energyentry_id_seq'::regclass)
 prop_id   | integer                  | not null
 timestamp | timestamp with time zone | not null
 value     | double precision         | not null
Indexes:
    "energy_energyentry_pkey" PRIMARY KEY, btree (id)
    "energy_energyentry_prop_id" btree (prop_id)
    "energy_energyentry_prop_id_timestamp_idx" btree (prop_id, "timestamp")
Foreign-key constraints:
    "energy_energyentry_prop_id_fkey" FOREIGN KEY (prop_id) REFERENCES gateway_peripheralproperty(id) DEFERRABLE INITIALLY DEFERRED

Os dados variam de 01-01-2012 até agora, com novos dados sendo constantemente adicionados. Existem cerca de 2,2 mil valores distintos na prop_idchave estrangeira, distribuídos uniformemente.

Percebo que as estimativas de linha não estão muito longe, mas as estimativas de custo parecem maiores pelo fator 4x. Provavelmente isso não é um problema, mas há algo que eu possa fazer sobre isso?

Espero que o acesso ao disco seja o problema, pois a tabela não fica na memória o tempo todo.

EXPLAIN ANALYZE 
SELECT SUM("value") 
FROM "energy_energyentry" 
WHERE 
  "prop_id"=82411 
  AND "timestamp">'2014-06-11' 
  AND "timestamp"<'2014-11-11'
;
 Aggregate  (cost=214481.45..214481.46 rows=1 width=8) (actual time=51504.814..51504.814 rows=1 loops=1)
   ->  Index Scan using energy_energyentry_prop_id_timestamp_idx on  energy_energyentry (cost=0.00..214434.08 rows=18947 width=8) (actual time=136.030..51488.321 rows=13578 loops=1)
         Index Cond: ((prop_id = 82411) AND ("timestamp" > '2014-06-11 00:00:00+00'::timestamp with time zone) AND ("timestamp" < '2014-11-11 00:00:00+00'::timestamp with time zone))
 Total runtime: 51504.841 ms

Alguma sugestão de como tornar isso mais rápido?
Também estou bem em ouvir que não fiz nada de estranho.

Exelian
fonte
1
Diga-nos como é a sua tabela, quais índices ela tem e a disseminação de dados.
Colin 't Hart
Eu adicionei a informação adicional que você pediu. Não sei se eu perdi alguma coisa.
Exelian 30/10
2
Estranho: sua explicação analisa os shows prop_time_idx, mas a definição da tabela mostra entry_prop_id_timestamp_idx. Este é o mesmo índice? Por favor conserte.
Colin 't Hart
Se você se referir por 'as estimativas de custo parecem ser um fator 4x maior' ao fato de que os números de custo são cerca de quatro vezes os do tempo real , observe que os dois não têm nada a ver um com o outro. O custo é apenas uma estimativa, ajudando o otimizador de consultas a escolher o plano mais bonito. Fora deste contexto, geralmente é um valor sem sentido.
Dezso
1
Quantos por cento da tabela seu período representa (sem levar em consideração os valores para prop)? Se apenas uma pequena porcentagem, talvez um índice ("timestamp", prop)fosse melhor. Vários índices com as mesmas colunas iniciais ( propno seu caso) também costumam ser redundantes.
Colin 't Hart

Respostas:

10

Sua tabela é grande , assim como qualquer índice que abranja toda a tabela. Assumindo que:

  • somente novos dados (com timestamp = now()) são inseridos
  • linhas existentes não são alteradas nem excluídas.
  • você tem dados desde 01/01/2012, mas as consultas são predominantemente no ano atual (?)

Eu sugeriria um índice parcial, com várias colunas (cobertura!) :

CREATE INDEX ON energy_energyentry (prop_id, "timestamp", value)
WHERE "timestamp" >= '2014-01-01 0:0';  -- adapt to your needs

Inclua apenas o período consultado regularmente. A eficácia se deteriora com o tempo com novas entradas. Recrie o índice de tempos em tempos. (Talvez você precise adaptar suas consultas.) Consulte a resposta vinculada abaixo.

O valor da última coluna é incluído apenas para obter verificações somente de índice . A configuração agressiva de autovacuum pode ajudar, mantendo o mapa de visibilidade atualizado, como @jjanes já mencionado .

O índice parcial deve caber na RAM mais facilmente e permanecer por mais tempo.

Pode ser necessário incluir essa WHEREcondição nas consultas para fazer o planejador entender que o índice é aplicável à consulta, como:

SELECT sum(value) AS sum_value
FROM   energy_energyentry
WHERE  prop_id = 82411 
AND   "timestamp" > '2014-06-11 0:0' 
AND   "timestamp" < '2014-11-11 0:0'
AND   "timestamp" >= '2014-01-01 0:0'; -- seems redundant, but may be needed

Como sua consulta está somando muitas linhas ( rows=13578), isso levará algum tempo, mesmo com uma verificação apenas de índice. No entanto, não deve demorar nem 50 segundos. Menos de um segundo em qualquer hardware decente.

Relacionados (mas ignore CLUSTERe FILLFACTOR, ambos são irrelevantes se você puder obter verificações somente de índice) :

Além disso:
como atualmente você tem um índice (prop_id, "timestamp"), o índice adicional (prop_id)pode custar mais do que vale:

Erwin Brandstetter
fonte
Agora que o Postgres suporta índices BRIN, isso seria útil aqui? Eu planejo armazenar cerca de 140 milhões de linhas nos dados do postgres. O BRIN é o índice certo para usar em tabelas tão grandes?
Arya #
2

Se você ativar o índice (prop_id, "timestamp", "value"), poderá usar uma varredura apenas do índice para calcular o valor sem nunca visitar a tabela. Isso pode economizar muito acesso aleatório ao disco.

Para obter o máximo benefício, você precisa ser agressivo ao aspirar a mesa. As configurações padrão do autovac não são agressivas o suficiente para tabelas somente de inserção nas quais você deseja suportar com eficiência as verificações apenas de índice.

jjanes
fonte
Acrescentar o valor pode ser realmente interessante, vou dar uma olhada se isso vai acelerar as coisas. Você tem alguma sugestão para configurações de vácuo ou documentação que eu possa consultar?
Exelian 30/10