Ich benutze PostgreSQL 9.1 unter Ubuntu 12.04.
Ich muss Datensätze innerhalb eines bestimmten Zeitraums auswählen: Meine Tabelle time_limits
enthält zwei timestamp
Felder und eine integer
Eigenschaft. In meiner aktuellen Tabelle befinden sich zusätzliche Spalten, die nicht mit dieser Abfrage verknüpft sind.
create table (
start_date_time timestamp,
end_date_time timestamp,
id_phi integer,
primary key(start_date_time, end_date_time,id_phi);
Diese Tabelle enthält ungefähr 2 Millionen Datensätze.
Abfragen wie die folgenden haben enorm viel Zeit in Anspruch genommen:
select * from time_limits as t
where t.id_phi=0
and t.start_date_time <= timestamp'2010-08-08 00:00:00'
and t.end_date_time >= timestamp'2010-08-08 00:05:00';
Also habe ich versucht, einen weiteren Index hinzuzufügen - die Umkehrung der PK:
create index idx_inversed on time_limits(id_phi, start_date_time, end_date_time);
Ich hatte den Eindruck, dass sich die Leistung verbessert hat: Die Zeit für den Zugriff auf Datensätze in der Mitte der Tabelle scheint angemessener zu sein: irgendwo zwischen 40 und 90 Sekunden.
Bei Werten in der Mitte des Zeitbereichs sind es jedoch noch einige zehn Sekunden. Und zweimal mehr, wenn Sie das Ende der Tabelle anvisieren (chronologisch gesehen).
Ich habe explain analyze
zum ersten Mal versucht , diesen Abfrageplan zu erhalten:
Bitmap Heap Scan on time_limits (cost=4730.38..22465.32 rows=62682 width=36) (actual time=44.446..44.446 rows=0 loops=1)
Recheck Cond: ((id_phi = 0) AND (start_date_time <= '2011-08-08 00:00:00'::timestamp without time zone) AND (end_date_time >= '2011-08-08 00:05:00'::timestamp without time zone))
-> Bitmap Index Scan on idx_time_limits_phi_start_end (cost=0.00..4714.71 rows=62682 width=0) (actual time=44.437..44.437 rows=0 loops=1)
Index Cond: ((id_phi = 0) AND (start_date_time <= '2011-08-08 00:00:00'::timestamp without time zone) AND (end_date_time >= '2011-08-08 00:05:00'::timestamp without time zone))
Total runtime: 44.507 ms
Siehe die Ergebnisse auf depesz.com.
Was kann ich tun, um die Suche zu optimieren? Sie können sehen, wie viel Zeit für das Durchsuchen der beiden Zeitstempelspalten aufgewendet wurde, wenn auf eingestellt id_phi
ist 0
. Und ich verstehe den großen Scan (60K Zeilen!) Auf den Zeitstempeln nicht. Werden sie nicht durch den Primärschlüssel indiziert und idx_inversed
ich fügte hinzu?
Sollte ich von Zeitstempeltypen zu etwas anderem wechseln?
Ich habe etwas über GIST- und GIN-Indizes gelesen. Ich gehe davon aus, dass sie unter bestimmten Bedingungen für benutzerdefinierte Typen effizienter sein können. Ist es eine praktikable Option für meinen Anwendungsfall?
quelle
explain analyze
Ausgabe angegebene Zeit ist die Zeit, die die Abfrage auf dem Server benötigt . Wenn Ihre Abfrage 45 Sekunden dauert, wird die zusätzliche Zeit für die Übertragung der Daten von der Datenbank an das Programm aufgewendet, in dem die Abfrage ausgeführt wird. Immerhin sind es 62682 Zeilen. Wenn jede Zeile groß ist (z. B. langvarchar
oder spaltenweisetext
), kann dies die Übertragungszeit beeinträchtigen drastisch.rows=62682 rows
ist die Schätzung des Planers . Die Abfrage gibt 0 Zeilen zurück.(actual time=44.446..44.446 rows=0 loops=1)
Antworten:
Für Postgres 9.1 oder höher:
In den meisten Fällen ist die Sortierreihenfolge eines Index kaum relevant. Postgres können praktisch genauso schnell rückwärts scannen. Bei Bereichsabfragen für mehrere Spalten kann dies jedoch einen großen Unterschied bewirken . Eng verwandt:
Betrachten Sie Ihre Frage:
Die Sortierreihenfolge der ersten Spalte
id_phi
im Index spielt keine Rolle. Da es auf Gleichheit geprüft wird (=
), sollte es an erster Stelle stehen. Das hast du richtig erkannt. Mehr in dieser verwandten Antwort:Postgres kann
id_phi = 0
in kürzester Zeit zu den folgenden zwei Spalten des übereinstimmenden Index springen . Diese werden mit Bereichsbedingungen der umgekehrten Sortierreihenfolge (<=
,>=
) abgefragt . In meinem Index stehen qualifizierende Zeilen an erster Stelle. Sollte mit einem B-Tree-Index 1 der schnellste Weg sein :start_date_time <= something
: index hat den frühesten Zeitstempel zuerst.Rekursieren Sie, bis die erste Zeile nicht mehr qualifiziert ist (superschnell).
end_date_time >= something
: index hat den neuesten Zeitstempel zuerst.Fahren Sie mit dem nächsten Wert für Spalte 2 fort.
Postgres kann vorwärts oder rückwärts scannen . So wie Sie den Index hatten, muss er alle übereinstimmenden Zeilen in den ersten beiden Spalten lesen und dann in der dritten filtern . Lesen Sie unbedingt
ORDER BY
das Kapitel Indexe und das Handbuch. Es passt ziemlich gut zu deiner Frage.Wie viele Zeilen stimmen in den ersten beiden Spalten überein?
Nur wenige mit
start_date_time
knapp vor dem Beginn des Zeitbereichs der Tabelle. Aber fast alle Zeilen mitid_phi = 0
am chronologischen Ende der Tabelle! Daher verschlechtert sich die Leistung mit späteren Startzeiten.Planer schätzt
Der Planer schätzt
rows=62682
für Ihre Beispielabfrage. Von diesen qualifizieren sich keine (rows=0
). Sie erhalten möglicherweise bessere Schätzungen, wenn Sie das Statistikziel für die Tabelle erhöhen. Für 2.000.000 Zeilen ...... könnte bezahlen. Oder noch höher. Mehr in dieser verwandten Antwort:
Ich vermute, Sie brauchen das nicht für
id_phi
(nur wenige unterschiedliche Werte, gleichmäßig verteilt), sondern für die Zeitstempel (viele unterschiedliche Werte, ungleich verteilt).Ich denke auch nicht, dass es für den verbesserten Index wichtig ist.
CLUSTER
/ pg_repackWenn Sie es dennoch schneller wollen, können Sie die physische Reihenfolge der Zeilen in Ihrer Tabelle optimieren. Wenn Sie es sich leisten können, Ihre Tabelle nur für einen kurzen Zeitraum (z. B. außerhalb der Geschäftszeiten) zu sperren, um Ihre Tabelle neu zu schreiben und Zeilen gemäß dem Index zu bestellen:
Berücksichtigen Sie bei gleichzeitigem Zugriff pg_repack , was auch ohne exklusive Sperre möglich ist.
In beiden Fällen müssen weniger Blöcke aus der Tabelle gelesen werden, und alles ist vorsortiert. Es ist ein einmaliger Effekt, der sich mit der Zeit verschlechtert, wenn Schreibvorgänge auf den Tisch die physische Sortierreihenfolge fragmentieren.
GiST-Index in Postgres 9.2+
1 Ab S. 9.2 gibt es eine weitere, möglicherweise schnellere Option: einen GiST-Index für eine Range-Spalte.
Es gibt integrierte Bereichstypen für
timestamp
undtimestamp with time zone
:tsrange
,tstzrange
. Ein Btree-Index ist in der Regel schneller für eine zusätzlicheinteger
Spalte wieid_phi
. Auch kleiner und billiger zu warten. Aber die Abfrage wird mit dem kombinierten Index insgesamt wahrscheinlich noch schneller sein.Ändern Sie Ihre Tabellendefinition oder verwenden Sie einen Ausdrucksindex .
Für den vorliegenden mehrspaltigen GiST-Index muss außerdem das Zusatzmodul
btree_gist
installiert sein (einmal pro Datenbank), das den Operator-Klassen die Möglichkeit bietet, ein einzuschließeninteger
.Die Trifecta! Ein mehrspaltiger funktionaler GiST-Index :
Verwenden Sie jetzt den Operator "enthält Bereich"
@>
in Ihrer Abfrage:SP-GiST-Index in Postgres 9.3+
Ein SP-GiST- Index könnte für diese Art von Abfrage sogar noch schneller sein - mit der Ausnahme, dass das Handbuch wie folgt zitiert wird :
In Postgres 12 immer noch wahr.
Sie müssten einen
spgist
Index für nur(tsrange(...))
mit einem zweitenbtree
Index für kombinieren(id_phi)
. Mit dem zusätzlichen Aufwand bin ich nicht sicher, ob dies konkurrieren kann.Verwandte Antwort mit einem Benchmark für nur eine
tsrange
Spalte:quelle
Erwins Antwort ist jedoch bereits umfassend:
Bereichstypen für Zeitstempel sind in PostgreSQL 9.1 mit der temporären Erweiterung von Jeff Davis verfügbar: https://github.com/jeff-davis/PostgreSQL-Temporal
Hinweis: Hat eingeschränkte Funktionen (verwendet Timestamptz, und Sie können nur den Stil '[)' überlappen lassen). Es gibt auch viele andere gute Gründe für ein Upgrade auf PostgreSQL 9.2.
quelle
Sie können versuchen, den mehrspaltigen Index in einer anderen Reihenfolge zu erstellen:
Ich habe einmal eine ähnliche Frage gestellt, die sich auch auf die Reihenfolge von Indizes in einem mehrspaltigen Index bezieht. Der Schlüssel ist, zuerst die restriktivsten Bedingungen zu verwenden, um den Suchraum zu reduzieren.
Edit : Mein Fehler. Jetzt sehe ich, dass Sie diesen Index bereits definiert haben.
quelle
Bitmap Index Scan on idx_time_limits_phi_start_end
Ich habe es geschafft, schnell zuzunehmen (von 1 Sekunde auf 70 ms)
Ich habe eine Tabelle mit Aggregationen von vielen Messungen und vielen Ebenen (
l
Spalte) (30s, 1m, 1h, usw.). Es gibt zwei bereichsgebundene Spalten:$s
für Anfang und$e
für Ende.Ich habe zwei mehrspaltige Indizes erstellt: einen für Start und einen für Ende.
Ich habe die Auswahlabfrage angepasst: Wähle Bereiche aus, in denen die Startgrenze im angegebenen Bereich liegt. Wählen Sie zusätzlich Bereiche aus, deren Endgrenze im angegebenen Bereich liegt.
Erklären Sie, dass zwei Zeilenströme mit unseren Indizes effizient verwendet werden.
Indizes:
Abfrage auswählen:
Erklären:
Der Trick ist, dass Ihre Planknoten nur gewünschte Zeilen enthalten. Zuvor hatten wir Tausende von Zeilen im Plan-Knoten, weil dieser ausgewählt
all points from some point in time to the very end
und dann vom nächsten Knoten nicht benötigte Zeilen entfernt wurde.quelle