Ich führe ein Update durch, bei dem ich eine genaue Gleichheit für eine tstzrange
Variable benötige . ~ 1 Million Zeilen werden geändert, und die Abfrage dauert ~ 13 Minuten. Das Ergebnis EXPLAIN ANALYZE
ist zu sehen, hier , und die tatsächlichen Ergebnisse sind sehr verschieden von denen der Anfrageplaner geschätzt. Das Problem besteht darin, dass der Index-Scan für t_range
eine einzelne Zeile erwartet, die zurückgegeben wird.
Dies scheint mit der Tatsache zusammenzuhängen, dass Statistiken zu Reichweitentypen anders gespeichert werden als solche anderer Typen. Mit Blick auf die pg_stats
Ansicht für die Spalte, n_distinct
-1 und anderen Bereichen (zB most_common_vals
, most_common_freqs
) sind leer.
Es müssen jedoch t_range
irgendwo Statistiken gespeichert sein . Ein extrem ähnliches Update, bei dem ich 'within' für t_range anstelle einer exakten Gleichheit verwende, dauert ungefähr 4 Minuten und verwendet einen wesentlich anderen Abfrageplan (siehe hier ). Der zweite Abfrageplan ist für mich sinnvoll, da jede Zeile in der temporären Tabelle und ein wesentlicher Teil der Verlaufstabelle verwendet werden. Noch wichtiger ist, dass der Abfrageplaner eine ungefähr korrekte Anzahl von Zeilen für den aktivierten Filter vorhersagt t_range
.
Die Verteilung von t_range
ist etwas ungewöhnlich. Ich verwende diese Tabelle, um den Verlaufsstatus einer anderen Tabelle zu speichern, und die Änderungen an der anderen Tabelle treten alle auf einmal in großen Speicherauszügen auf, sodass es nicht viele unterschiedliche Werte von gibt t_range
. Hier sind die Zählungen, die jedem der eindeutigen Werte von entsprechen 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
Die oben angegebenen Zählungen t_range
sind vollständig, sodass die Kardinalität ~ 3M beträgt (von denen ~ 1M von beiden Aktualisierungsabfragen betroffen sind).
Warum ist die Leistung von Abfrage 1 viel schlechter als die von Abfrage 2? In meinem Fall ist Abfrage 2 ein guter Ersatz, aber wenn wirklich eine exakte Bereichsgleichheit erforderlich war, wie kann ich Postgres dazu bringen, einen intelligenteren Abfrageplan zu verwenden?
Tabellendefinition mit Indizes (Löschen irrelevanter Spalten):
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)
Abfrage 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;
Abfrage 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;
Q1 aktualisiert 999753 Zeilen und Q2 aktualisiert 999753 + 36791 = 1036544 (dh die temporäre Tabelle ist so, dass jede Zeile, die der Zeitbereichsbedingung entspricht, aktualisiert wird).
Ich habe diese Abfrage als Antwort auf den Kommentar von @ ypercube versucht :
Abfrage 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;
Der Abfrageplan und die Ergebnisse (siehe hier ) lagen zwischen den beiden vorherigen Fällen (~ 6 Minuten).
2016/02/05 EDIT
Nachdem ich nach 1,5 Jahren keinen Zugriff mehr auf die Daten hatte, erstellte ich eine Testtabelle mit derselben Struktur (ohne Indizes) und ähnlicher Kardinalität. jjanes 'antwort schlug vor, dass die ursache die ordnung der temporären tabelle sein könnte, die für die aktualisierung verwendet wurde. Ich konnte die Hypothese nicht direkt testen, da ich keinen Zugriff darauf habe track_io_timing
(mit Amazon RDS).
Die Gesamtergebnisse waren viel schneller (um einen Faktor von mehreren). Ich vermute, das liegt an der Entfernung der Indizes, was mit Erwins Antwort übereinstimmt .
In diesem Testfall haben die Abfragen 1 und 2 im Wesentlichen dieselbe Zeit in Anspruch genommen, da beide den Merge-Join verwendet haben. Das heißt, ich konnte nicht auslösen, was auch immer Postgres veranlasste, den Hash-Join zu wählen. Daher habe ich keine Klarheit darüber, warum Postgres den Hash-Join mit schlechter Leistung überhaupt gewählt hat.
(lower(t_range),upper(t_range))
da Sie die Gleichheit prüfen.
(a = b)
zu zwei „enthält“ Bedingungen:(a @> b AND b @> a)
? Ändert sich der Plan?