2012-12-04 29 views
7

Ho 2 tabelle in PostgreSQL 9.1 - flight_2012_09_12 contenenti circa 500.000 righe e position_2012_09_12 contenenti circa 5,5 milioni di righe. Sto eseguendo una semplice query di join e ci vorrà molto tempo per completarla e, nonostante il fatto che i tavoli non siano piccoli, sono convinto che ci saranno alcuni importanti vantaggi nell'esecuzione.Ottimizza query postgresql

La query è:

SELECT f.departure, f.arrival, 
     p.callsign, p.flightkey, p.time, p.lat, p.lon, p.altitude_ft, p.speed 
FROM position_2012_09_12 AS p 
JOIN flight_2012_09_12 AS f 
    ON p.flightkey = f.flightkey 
WHERE p.lon < 0 
     AND p.time BETWEEN '2012-9-12 0:0:0' AND '2012-9-12 23:0:0' 

L'uscita del spiegano analizzare è:

Hash Join (cost=239891.03..470396.82 rows=4790498 width=51) (actual time=29203.830..45777.193 rows=4403717 loops=1) 
Hash Cond: (f.flightkey = p.flightkey) 
-> Seq Scan on flight_2012_09_12 f (cost=0.00..1934.31 rows=70631 width=12) (actual time=0.014..220.494 rows=70631 loops=1) 
-> Hash (cost=158415.97..158415.97 rows=3916885 width=43) (actual time=29201.012..29201.012 rows=3950815 loops=1) 
    Buckets: 2048 Batches: 512 (originally 256) Memory Usage: 1025kB 
    -> Seq Scan on position_2012_09_12 p (cost=0.00..158415.97 rows=3916885 width=43) (actual time=0.006..14630.058 rows=3950815 loops=1) 
      Filter: ((lon < 0::double precision) AND ("time" >= '2012-09-12 00:00:00'::timestamp without time zone) AND ("time" <= '2012-09-12 23:00:00'::timestamp without time zone)) 
Total runtime: 58522.767 ms 

Credo che il problema si trova con la scansione sequenziale sul tavolo posizione, ma non riesco a capire perché È lì. Le strutture della tabella con gli indici sono al di sotto:

   Table "public.flight_2012_09_12" 
    Column  |   Type    | Modifiers 
--------------------+-----------------------------+----------- 
callsign   | character varying(8)  | 
flightkey   | integer      | 
source    | character varying(16)  | 
departure   | character varying(4)  | 
arrival   | character varying(4)  | 
original_etd  | timestamp without time zone | 
original_eta  | timestamp without time zone | 
enroute   | boolean      | 
etd    | timestamp without time zone | 
eta    | timestamp without time zone | 
equipment   | character varying(6)  | 
diverted   | timestamp without time zone | 
time    | timestamp without time zone | 
lat    | double precision   | 
lon    | double precision   | 
altitude   | character varying(7)  | 
altitude_ft  | integer      | 
speed    | character varying(4)  | 
asdi_acid   | character varying(4)  | 
enroute_eta  | timestamp without time zone | 
enroute_eta_source | character varying(1)  | 
Indexes: 
"flight_2012_09_12_flightkey_idx" btree (flightkey) 
"idx_2012_09_12_altitude_ft" btree (altitude_ft) 
"idx_2012_09_12_arrival" btree (arrival) 
"idx_2012_09_12_callsign" btree (callsign) 
"idx_2012_09_12_departure" btree (departure) 
"idx_2012_09_12_diverted" btree (diverted) 
"idx_2012_09_12_enroute_eta" btree (enroute_eta) 
"idx_2012_09_12_equipment" btree (equipment) 
"idx_2012_09_12_etd" btree (etd) 
"idx_2012_09_12_lat" btree (lat) 
"idx_2012_09_12_lon" btree (lon) 
"idx_2012_09_12_original_eta" btree (original_eta) 
"idx_2012_09_12_original_etd" btree (original_etd) 
"idx_2012_09_12_speed" btree (speed) 
"idx_2012_09_12_time" btree ("time") 

      Table "public.position_2012_09_12" 
Column |   Type    | Modifiers 
-------------+-----------------------------+----------- 
callsign | character varying(8)  | 
flightkey | integer      | 
time  | timestamp without time zone | 
lat   | double precision   | 
lon   | double precision   | 
altitude | character varying(7)  | 
altitude_ft | integer      | 
course  | integer      | 
speed  | character varying(4)  | 
trackerkey | integer      | 
the_geom | geometry     | 
Indexes: 
"index_2012_09_12_altitude_ft" btree (altitude_ft) 
"index_2012_09_12_callsign" btree (callsign) 
"index_2012_09_12_course" btree (course) 
"index_2012_09_12_flightkey" btree (flightkey) 
"index_2012_09_12_speed" btree (speed) 
"index_2012_09_12_time" btree ("time") 
"position_2012_09_12_flightkey_idx" btree (flightkey) 
"test_index" btree (lon) 
"test_index_lat" btree (lat) 

non riesco a pensare a nessun altro modo per riscrivere la query e quindi sono perplesso a questo punto. Se la configurazione corrente è buona come è, ma mi sembra che dovrebbe essere molto più veloce di quello che è attualmente. Qualsiasi aiuto sarebbe molto apprezzato.

+0

Puoi fornire statistiche sulle colonne public.position_2012_09_12 tabella lon e time? Forse un po '(tempo) in cui l'indice lon <0 aiuterà, ma ci sono 3950815 righe nella tabella delle posizioni che corrispondono a queste condizioni. Ci sono molti più dati in questa tabella? – sufleR

+0

Ci sono 5563070 righe in quella tabella (ho modificato il mio post per riflettere quello invece dei 3,5 milioni che ho originariamente dichiarato) – TheOx

+2

Quale versione di Postgresql stai usando? – plang

risposta

2

Il motivo per cui si ottiene una scansione sequenziale è che Postgres ritiene che leggerà meno pagine del disco in quel modo rispetto all'utilizzo di indici. Probabilmente ha ragione Considerare, se si utilizza un indice non coprente, è necessario leggere tutte le pagine indice corrispondenti. essenzialmente emette un elenco di identificativi di riga. Il motore DB deve quindi leggere ciascuna delle pagine di dati corrispondenti.

La tabella delle posizioni utilizza 71 byte per riga, più ciò che viene preso da un tipo geom (presumo 16 byte per illustrazione), ovvero 87 byte. Una pagina Postgres è 8192 byte. Quindi hai circa 90 righe per pagina.

La query corrisponde a 3950815 su 5563070 righe o circa il 70% del totale.Supponendo che i dati siano distribuiti casualmente, per quanto riguarda i filtri dove si trovano, c'è una probabilità quasi del 30%^90 di trovare una pagina di dati senza righe corrispondenti. Questo è essenzialmente nulla. Quindi, a prescindere dalla qualità dei tuoi indici, dovrai comunque leggere tutte le pagine di dati. Se devi leggere comunque tutte le pagine, solitamente una scansione della tabella è un buon approccio.

Quello che esce qui, è che ho detto indice non coprente. Se si è pronti a creare indici in grado di rispondere alle query da soli, è possibile evitare di cercare le pagine di dati, quindi tornare al gioco. Vorrei suggerire quanto segue:

flight_2012_09_12 (flightkey, departure, arrival) 
position_2012_09_12 (filghtkey, time, lon, ...) 
position_2012_09_12 (lon, time, flightkey, ...) 
position_2012_09_12 (time, long, flightkey, ...) 

I punti qui rappresentano il resto delle colonne che si sta selezionando. Avrai bisogno solo di uno degli indici in posizione, ma è difficile dire quale sarà il migliore. Il primo approccio potrebbe consentire un join di unione su dati preordinati, con il costo di leggere l'intero secondo indice per eseguire il filtraggio. Il secondo e il terzo consentiranno il prefiltraggio dei dati, ma richiedono un hash join. Dare quanto del costo sembra essere nel join dell'hash, il join di unione potrebbe essere una buona opzione.

Poiché la query richiede 52 degli 87 byte per riga e gli indici hanno overhead, è possibile che l'indice non occupi molto spazio, se non nessuno, della tabella stessa.

Un altro approccio consiste nell'attaccare il lato "distribuito a caso" di esso, esaminando il clustering.

+1

Non mi sembrava che sarebbe valso la pena aggiungere un indice di copertura sul tavolo di volo, dato che una scansione completa sembra richiedere solo 220ms? –

+0

@DavidAldridge Punto giusto, anche se l'indice di copertura che inizia con la chiave di volo su entrambe le tabelle potrebbe consentire un join di unione, che mi aspetto essere più veloce dell'hash join su dati pre-ordinati. – Laurence

+0

@DavidAldridge L'utente è su PostgreSQL 9.1, che non ha scansioni di solo indice (come gli indici di copertura), quindi il problema è comunque valido. –

3

Le stime di conteggio delle righe sono abbastanza ragionevoli, quindi dubito che questo sia un problema di statistiche.

mi piacerebbe provare:

  • creazione di un indice sulla position_2012_09_12(lon,"time") o forse un indice parziale sul position_2012_09_12("time") WHERE (lon < 0) se abitualmente cercare lon < 0.

  • Impostazione random_page_cost inferiore, forse 1,1. Vedi se (a) questo cambia il piano e (b) se il nuovo piano è effettivamente più veloce. A scopo di test per vedere se evitare un seqscan sarebbe più veloce è possibile SET enable_seqscan = off; se lo è, cambia i parametri di costo.

  • Aumentare work_mem per questa query. SET work_mem = 10M o qualcosa prima di eseguirlo.

  • Eseguire l'ultimo PostgreSQL se non lo si è già. Specifica sempre la tua versione di PostgreSQL in domande. (Aggiornamento dopo modifica): sei su 9.1; va bene. Il più grande miglioramento delle prestazioni in 9.2 è stato di scansioni di solo indice, e non sembra probabile che trarrai grandi benefici dalle scansioni di solo indice per questa query.

Migliorerai anche le prestazioni se riesci a sbarazzarti delle colonne per restringere le righe. Non farà tonnellate di differenza, ma ne farà un po '.