Ich habe eine Tabelle mit 165 Millionen Datensätzen wie folgt:
Performance
id integer
installs integer
hour timestamp without time zone
Ich habe auch einen Index zur Stunde:
CREATE INDEX hour_idx
ON performance
USING btree
(hour DESC NULLS LAST);
Die Auswahl der 10 nach Stunden geordneten Top-Datensätze dauert jedoch 6 Minuten!
EXPLAIN ANALYZE select hour from performance order by hour desc limit 10
Kehrt zurück
Limit (cost=7952135.23..7952135.25 rows=10 width=8) (actual time=376313.958..376313.964 rows=10 loops=1)
-> Sort (cost=7952135.23..8368461.00 rows=166530310 width=8) (actual time=376313.957..376313.960 rows=10 loops=1)
Sort Key: hour
Sort Method: top-N heapsort Memory: 25kB
-> Seq Scan on performance (cost=0.00..4353475.10 rows=166530310 width=8) (actual time=0.006..327149.828 rows=192330557 loops=1)
Planning time: 0.070 ms
Execution time: 376330.573 ms
Warum dauert das so lange? Wenn es einen Index für das Datumsfeld desc gibt - sollte es nicht superschnell sein, Daten abzurufen?
postgresql
performance
index
explain
Dejell
quelle
quelle
VACUUM ANALYZE performance;
?select hour from performance order by hour desc nulls last limit 10;
?Antworten:
In Ihrem obigen Beispielcode wird der Index explizit als erstellt
NULLS LAST
und die Abfrage wird implizit ausgeführtNULLS FIRST
(was die Standardeinstellung istORDER BY .. DESC
), sodass PostgreSQL die Daten neu sortieren müsste, wenn der Index verwendet würde. Infolgedessen würde der Index die Abfrage tatsächlich um ein Vielfaches langsamer machen als selbst der (bereits langsame) Tabellenscan.Wir können
WHERE
der Stundenspalte eine Klausel hinzufügen, damit die Verwendung des Index eine gute Idee wird. Beachten Sie jedoch, dass wir die Daten aus dem Index noch neu sortieren müssen.Wenn wir
NULLS LAST
Ihrer Abfrage ein explizites Element hinzufügen , wird der Index wie erwartet verwendet.Wenn wir alternativ das (nicht standardmäßige)
NULLS LAST
aus Ihrem Index löschen, wird es von der Abfrage wie erwartet ohne Änderung verwendet.Beachten Sie, dass Sie das auch
DESC
aus Ihrem Index löschen können. PostgreSQL kann Indizes sowohl vorwärts als auch rückwärts scannen, und bei einspaltigen Indizes ist es im Allgemeinen nicht erforderlich, sie umzukehren. Sie müssen nur vorsichtig sein, wenn Sie die richtige Kombination aus Reihenfolge und Nullen zuerst / zuletzt haben.quelle
Wenn die meisten Ihrer Abfragen beabsichtigen nicht-NULL - Werte wählen Sie aus ,
hour
dann sollten Sie erwägen , einen Aufbau teilweise auf diesen Werten Index, also so etwas wie:Solange Sie entweder nach einem bestimmten Wert von
hour
fragen, wie Jeremy in seiner Antwort gezeigt hat, oderhour IS NOT NULL
IhreWHERE
Klausel ergänzen , erhalten Sie dieselben Ergebnisse und sparen möglicherweise auch ein wenig Platz:Wenn
NULL
die Spalte keine Werte enthält, sollten Sie sie deklarierenNOT NULL
(ich gehe davon aus, dass Sie wissen, wie dies mit ALTER TABLE gemacht wird; o)), und dann den Index erstellen (ohneNULLS LAST
, da dies sowieso nicht mehr wichtig ist). Dann erhalten Sie den gleichen Vorteil:quelle
NOT NULL
Wenn Sie mit der deklarierten Spalte den Index ohne Verweis aufNULLS
first oder last erstellen und ebenfalls keinen Verweis in der Abfrage angeben, erhalten Sie jedes Mal den Nur-Index-Scan.