L'indice lento esegue la scansione in una tabella di grandi dimensioni


12

Usando PostgreSQL 9.2, ho problemi con le query lente su una tabella relativamente grande (oltre 200 milioni di righe). Non sto provando niente di folle, sto solo aggiungendo valori storici. Di seguito è riportata la query e l'output del piano di query.

Il mio layout del tavolo:

                                   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

I dati vanno dal 01-01-2012 ad oggi, con l'aggiunta costante di nuovi dati. Ci sono circa 2.2k valori distinti nella prop_idchiave esterna, distribuiti uniformemente.

Ho notato che le stime delle righe non sono lontane, ma le stime dei costi sembrano maggiori del fattore 4x. Questo probabilmente non è un problema, ma c'è qualcosa che potrei fare al riguardo?

Mi aspetto che l'accesso al disco potrebbe essere il problema, poiché la tabella non è in memoria per tutto il tempo.

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

Qualche suggerimento su come renderlo più veloce?
Sto bene anche solo sentendo che non ho fatto nulla di strano.


1
Comunicaci come appare la tua tabella, quali indici ha e la diffusione dei dati.
Colin 't Hart,

Ho aggiunto le informazioni aggiuntive che hai chiesto. Non so se mi sono perso qualcosa.
Exelian,

2
Strano: la tua spiegazione analizza mostra prop_time_idx, ma mostra la definizione della tabella entry_prop_id_timestamp_idx. È lo stesso indice? Per favore, aggiusta.
Colin 't Hart,

Se fai riferimento a "le stime dei costi sembrano essere un fattore 4x più grande" del fatto che i numeri di costo sono circa 4 volte quelli del tempo effettivo , ti preghiamo di notare che i due non hanno nulla a che fare l'uno con l'altro. Il costo è solo una stima, aiutando l'ottimizzatore di query a scegliere il piano più bello. Al di fuori di questo contesto, di solito è un valore insignificante.
dezso,

1
Quanta percentuale della tabella rappresenta l'intervallo di date (senza prendere in considerazione i valori per prop)? Se solo una piccola percentuale, forse un indice su ("timestamp", prop)sarebbe meglio. Anche gli indici multipli con le stesse colonne iniziali ( propnel tuo caso) sono spesso ridondanti.
Colin 't Hart,

Risposte:


10

La tua tabella è grande , così come qualsiasi indice che copre l'intera tabella. Supponendo che:

  • timestamp = now()vengono inseriti solo i nuovi dati (con )
  • le righe esistenti non vengono né modificate né eliminate.
  • hai dati dal 01-01-2012 ma le domande sono prevalentemente sull'anno corrente (?)

Vorrei suggerire un indice parziale, multi-colonna (che copre!) :

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

Includere solo l'intervallo di tempo richiesto regolarmente. L'efficacia si deteriora nel tempo con nuove voci. Ricreare l'indice di volta in volta. (Potrebbe essere necessario adattare le tue query.) Vedi la risposta collegata di seguito.

L'ultimo valore della colonna è incluso solo per ottenere scansioni solo indice da questo. L'impostazione aggressiva del vuoto automatico può essere utile mantenendo aggiornata la mappa di visibilità, come già indicato da @jjanes .

L'indice parziale dovrebbe adattarsi più facilmente alla RAM e rimanere lì più a lungo.

Potrebbe essere necessario includere questa WHEREcondizione nelle query per far capire al pianificatore che l'indice è applicabile alla query, come:

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

Poiché la tua query sta sommando molte righe ( rows=13578), ci vorrà del tempo, anche con una scansione solo indice. Tuttavia, non dovrebbe essere vicino ai 50 secondi. Meno di un secondo su qualsiasi hardware decente a metà strada.

Correlati (ma ignora CLUSTERe FILLFACTOR, entrambi sono irrilevanti se puoi ottenere scansioni solo dell'indice da questo) :

A parte:
dato che al momento hai un indice attivo(prop_id, "timestamp") , l'indice aggiuntivo (prop_id)potrebbe costare solo più del suo valore:


Ora che Postgres supporta gli indici BRIN, sarebbe utile qui? Ho intenzione di archiviare circa 140 milioni di righe sui dati su Postgres, BRIN è l'indice giusto da usare per una tabella così grande?
Arya,

2

Se si esegue l'indice su (prop_id, "timestamp", "value"), è possibile utilizzare una scansione di solo indice per calcolare il valore senza mai visitare la tabella. Questo potrebbe salvare un sacco di accesso casuale al disco.

Per ottenere il massimo beneficio, devi essere aggressivo nell'aspirare il tavolo. Le impostazioni predefinite di autovac non sono abbastanza aggressive per le tabelle di solo inserimento su cui si desidera supportare in modo efficiente le scansioni solo indice.


Aggiungere il valore potrebbe essere davvero interessante, darò un'occhiata se questo accelererà le cose. Hai qualche suggerimento per le impostazioni del vuoto o la documentazione che posso consultare?
Exelian,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.