Sto eseguendo un aggiornamento in cui ho bisogno di una parità esatta su una tstzrange
variabile. Vengono modificate ~ 1 milione di righe e la query richiede ~ 13 minuti. Il risultato di EXPLAIN ANALYZE
può essere visto qui e i risultati effettivi sono estremamente diversi da quelli stimati dal pianificatore di query. Il problema è che la scansione dell'indice su si t_range
aspetta che venga restituita una singola riga.
Ciò sembra essere correlato al fatto che le statistiche sui tipi di intervallo sono memorizzate in modo diverso da quelle di altri tipi. Guardando la pg_stats
vista per la colonna, n_distinct
è -1 e altri campi (ad esempio most_common_vals
, most_common_freqs
) sono vuoti.
Tuttavia, ci devono essere statistiche archiviate da t_range
qualche parte. Un aggiornamento estremamente simile in cui utilizzo un 'entro' su t_range anziché una parità esatta richiede circa 4 minuti per essere eseguito e utilizza un piano di query sostanzialmente diverso (vedi qui ). Il secondo piano di query ha senso per me perché verranno utilizzate tutte le righe nella tabella temporanea e una parte sostanziale della tabella cronologica. Ancora più importante, il pianificatore di query prevede un numero approssimativamente corretto di righe per il filtro attivo t_range
.
La distribuzione di t_range
è un po 'insolita. Sto usando questa tabella per memorizzare lo stato storico di un'altra tabella e le modifiche all'altra tabella si verificano tutte contemporaneamente in grandi discariche, quindi non ci sono molti valori distinti di t_range
. Ecco i conteggi corrispondenti a ciascuno dei valori univoci di t_range
:
t_range | count
-------------------------------------------------------------------+---------
["2014-06-12 20:58:21.447478+00","2014-06-27 07:00:00+00") | 994676
["2014-06-12 20:58:21.447478+00","2014-08-01 01:22:14.621887+00") | 36791
["2014-06-27 07:00:00+00","2014-08-01 07:00:01+00") | 1000403
["2014-06-27 07:00:00+00",infinity) | 36791
["2014-08-01 07:00:01+00",infinity) | 999753
I conteggi per i distinti t_range
precedenti sono completi, quindi la cardinalità è ~ 3M (di cui ~ 1M sarà influenzato da entrambe le query di aggiornamento).
Perché la query 1 ha prestazioni molto più scarse rispetto alla query 2? Nel mio caso, la query 2 è un buon sostituto, ma se fosse veramente necessaria un'eguaglianza di intervallo esatta, come potrei fare in modo che Postgres utilizzi un piano di query più intelligente?
Definizione della tabella con indici (eliminazione di colonne non pertinenti):
Column | Type | Modifiers
---------------------+-----------+------------------------------------------------------------------------------
history_id | integer | not null default nextval('gtfs_stop_times_history_history_id_seq'::regclass)
t_range | tstzrange | not null
trip_id | text | not null
stop_sequence | integer | not null
shape_dist_traveled | real |
Indexes:
"gtfs_stop_times_history_pkey" PRIMARY KEY, btree (history_id)
"gtfs_stop_times_history_t_range" gist (t_range)
"gtfs_stop_times_history_trip_id" btree (trip_id)
Query 1:
UPDATE gtfs_stop_times_history sth
SET shape_dist_traveled = tt.shape_dist_traveled
FROM gtfs_stop_times_temp tt
WHERE sth.trip_id = tt.trip_id
AND sth.stop_sequence = tt.stop_sequence
AND sth.t_range = '["2014-08-01 07:00:01+00",infinity)'::tstzrange;
Query 2:
UPDATE gtfs_stop_times_history sth
SET shape_dist_traveled = tt.shape_dist_traveled
FROM gtfs_stop_times_temp tt
WHERE sth.trip_id = tt.trip_id
AND sth.stop_sequence = tt.stop_sequence
AND '2014-08-01 07:00:01+00'::timestamptz <@ sth.t_range;
Aggiornamenti Q1 999753 righe e aggiornamenti Q2 999753 + 36791 = 1036544 (ovvero, la tabella temporanea è tale che ogni riga corrispondente alla condizione dell'intervallo di tempo viene aggiornata).
Ho provato questa query in risposta al commento di @ ypercube :
Query 3:
UPDATE gtfs_stop_times_history sth
SET shape_dist_traveled = tt.shape_dist_traveled
FROM gtfs_stop_times_temp tt
WHERE sth.trip_id = tt.trip_id
AND sth.stop_sequence = tt.stop_sequence
AND sth.t_range <@ '["2014-08-01 07:00:01+00",infinity)'::tstzrange
AND '["2014-08-01 07:00:01+00",infinity)'::tstzrange <@ sth.t_range;
Il piano di query e i risultati (vedi qui ) erano intermedi tra i due casi precedenti (~ 6 minuti).
05/02/2016 EDIT
Non avendo più accesso ai dati dopo 1,5 anni, ho creato una tabella di test con la stessa struttura (senza indici) e cardinalità simile. La risposta di jjanes ha proposto che la causa potrebbe essere l'ordinamento della tabella temporanea utilizzata per l'aggiornamento. Non sono stato in grado di verificare direttamente l'ipotesi perché non ho accesso a track_io_timing
(utilizzando Amazon RDS).
I risultati complessivi sono stati molto più rapidi (di un fattore di diversi). Immagino che ciò sia dovuto alla rimozione degli indici, in linea con la risposta di Erwin .
In questo caso di test, le query 1 e 2 hanno impiegato sostanzialmente lo stesso tempo, poiché entrambi hanno utilizzato l'unione di tipo merge. Cioè, non sono stato in grado di innescare ciò che stava causando a Postgres la scelta dell'hash join, quindi non ho chiarezza sul motivo per cui Postgres ha scelto l'hash join con prestazioni scarse in primo luogo.
(lower(t_range),upper(t_range))
dal momento che si controlla l'uguaglianza.
(a = b)
a due "contiene" condizioni:(a @> b AND b @> a)
? Il piano cambia?