Wie zwinge ich Postgres, einen Index zu verwenden, wenn es sonst darauf bestehen würde, einen sequentiellen Scan durchzuführen?
Wie zwinge ich Postgres, einen Index zu verwenden, wenn es sonst darauf bestehen würde, einen sequentiellen Scan durchzuführen?
Antworten:
Angenommen, Sie fragen nach der allgemeinen Funktion "Indexhinweise", die in vielen Datenbanken zu finden ist, bietet PostgreSQL eine solche Funktion nicht an. Dies war eine bewusste Entscheidung des PostgreSQL-Teams. Eine gute Übersicht darüber, warum und was Sie stattdessen tun können, finden Sie hier . Die Gründe dafür sind im Grunde, dass es sich um einen Performance-Hack handelt, der später zu weiteren Problemen führt, wenn sich Ihre Daten ändern, während der Optimierer von PostgreSQL den Plan basierend auf den Statistiken neu bewerten kann. Mit anderen Worten, was heute ein guter Abfrageplan sein könnte, wird wahrscheinlich nicht für alle Zeiten ein guter Abfrageplan sein, und Indexhinweise erzwingen einen bestimmten Abfrageplan für alle Zeiten.
Als sehr stumpfer Hammer, der zum Testen nützlich ist, können Sie die Parameter enable_seqscan
und verwenden enable_indexscan
. Sehen:
Diese sind nicht für den laufenden Produktionseinsatz geeignet . Wenn Sie Probleme mit der Auswahl des Abfrageplans haben, sollten Sie die Dokumentation zum Aufspüren von Problemen mit der Abfrageleistung lesen . enable_
Stellen Sie nicht nur Parameter ein und gehen Sie weg.
Wenn Sie keinen guten Grund für die Verwendung des Index haben, trifft Postgres möglicherweise die richtige Wahl. Warum?
Siehe auch diesen alten Newsgroup-Beitrag .
Wahrscheinlich der einzig gültige Grund für die Verwendung
set enable_seqscan=false
Dies ist der Fall, wenn Sie Abfragen schreiben und schnell sehen möchten, wie der Abfrageplan tatsächlich aussehen würde, wenn große Datenmengen in den Tabellen enthalten wären. Oder natürlich, wenn Sie schnell bestätigen müssen, dass Ihre Abfrage keinen Index verwendet, nur weil der Datensatz zu klein ist.
set enable_seqscan=false
, Ausführen Ihrer Abfrage und dann schnell ausführen set enable_seqscan=true
, um postgresql wieder in das richtige Verhalten zu versetzen (und dies natürlich nicht in der Produktion, sondern nur in der Entwicklung!)
SET SESSION enable_seqscan=false
nur sich selbst zu beeinflussen
Manchmal trifft PostgreSQL nicht die beste Auswahl an Indizes für eine bestimmte Bedingung. Angenommen, es gibt eine Transaktionstabelle mit mehreren Millionen Zeilen, von denen es für einen bestimmten Tag mehrere Hundert gibt, und die Tabelle enthält vier Indizes: transaction_id, client_id, date und description. Sie möchten die folgende Abfrage ausführen:
SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
description = 'Refund'
GROUP BY client_id
PostgreSQL verwendet möglicherweise den Index transaction_description_idx anstelle von transaction_date_idx. Dies kann dazu führen, dass die Abfrage mehrere Minuten statt weniger als einer Sekunde dauert. Wenn dies der Fall ist, können Sie die Verwendung des Index am Datum erzwingen, indem Sie die Bedingung wie folgt verfälschen:
SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
description||'' = 'Refund'
GROUP BY client_id
your_wanted_index
. Es kann daher sein, dass die postgresql-Engine stattdessen nur einen Sequenz- / Primärschlüssel-Scan durchführt. Schlussfolgerung - Es gibt keine 100% zuverlässige Methode, um eine Indexverwendung für den PostgreSql-Server zu erzwingen.
where
Bedingung außer zwei Tabellen oder verknüpften gibt und Postgres den Index nicht übernimmt ?
Dieses Problem tritt normalerweise auf, wenn die geschätzten Kosten eines Index-Scans zu hoch sind und die Realität nicht korrekt widerspiegeln. Möglicherweise müssen Sie den random_page_cost
Konfigurationsparameter verringern, um dies zu beheben. Aus der Postgres-Dokumentation :
Wenn Sie diesen Wert [...] reduzieren, bevorzugt das System Index-Scans. Durch Erhöhen werden Index-Scans relativ teuer.
Sie können überprüfen, ob ein niedrigerer Wert tatsächlich dazu führt, dass Postgres den Index verwendet (dies wird jedoch nur zum Testen verwendet ):
EXPLAIN <query>; # Uses sequential scan
SET random_page_cost = 1;
EXPLAIN <query>; # May use index scan now
Sie können den Standardwert mit SET random_page_cost = DEFAULT;
wieder herstellen.
Index-Scans erfordern nicht sequentielle Abrufe von Festplattenseiten. Postgres verwendet random_page_cost
, um die Kosten solcher nicht sequentiellen Abrufe im Verhältnis zu sequentiellen Abrufen zu schätzen. Der Standardwert ist 4.0
, wobei ein durchschnittlicher Kostenfaktor von 4 im Vergleich zu sequentiellen Abrufen angenommen wird (unter Berücksichtigung von Caching-Effekten).
Das Problem ist jedoch, dass dieser Standardwert in den folgenden wichtigen realen Szenarien ungeeignet ist:
1) Solid-State-Laufwerke
Wie die Dokumentation zugibt:
Speicher mit geringen zufälligen Lesekosten im Vergleich zu sequentiellen Laufwerken, z. B. Solid-State-Laufwerken, können möglicherweise besser mit einem niedrigeren Wert für modelliert werden
random_page_cost
.
Laut dem letzten Punkt dieser Folie aus einem Vortrag auf der PostgresConf 2018 random_page_cost
sollte auf etwas zwischen 1.0
und 2.0
für Solid-State-Laufwerke eingestellt werden.
2) Zwischengespeicherte Daten
Wenn die erforderlichen Indexdaten bereits im RAM zwischengespeichert sind, ist ein Index-Scan immer erheblich schneller als ein sequentieller Scan. Die Dokumentation sagt:
Entsprechend
random_page_cost
kann eine [...] Verringerung angemessen sein , wenn sich Ihre Daten wahrscheinlich vollständig im Cache befinden.
Das Problem ist, dass Sie natürlich nicht leicht wissen können, ob die relevanten Daten bereits zwischengespeichert sind. Wenn jedoch häufig ein bestimmter Index abgefragt wird und das System über ausreichend RAM verfügt, werden die Daten wahrscheinlich zwischengespeichert und random_page_cost
sollten auf einen niedrigeren Wert gesetzt werden. Sie müssen mit verschiedenen Werten experimentieren und sehen, was für Sie funktioniert.
Möglicherweise möchten Sie auch die Erweiterung pg_prewarm für das explizite Zwischenspeichern von Daten verwenden.
Die Frage an sich ist sehr ungültig. Das Erzwingen (zum Beispiel durch enable_seqscan = off) ist eine sehr schlechte Idee. Es kann nützlich sein zu überprüfen, ob es schneller sein wird, aber Produktionscode sollte niemals solche Tricks verwenden.
Erklären Sie stattdessen die Analyse Ihrer Abfrage, lesen Sie sie und finden Sie heraus, warum PostgreSQL (Ihrer Meinung nach) einen schlechten Plan wählt.
Es gibt Tools im Web, die beim Lesen helfen, die Analyse zu erklären - eine davon ist EXPLAIN.depesz.com - von mir geschrieben.
Eine andere Möglichkeit besteht darin, sich dem # postgresql-Kanal im freenode irc-Netzwerk anzuschließen und mit den dortigen Mitarbeitern zu sprechen, um Ihnen zu helfen. Bei der Optimierung der Abfrage geht es nicht darum, "eine Frage zu stellen, eine Antwort zu erhalten, glücklich zu sein". Es ist eher ein Gespräch, bei dem viele Dinge überprüft und viele Dinge gelernt werden müssen.
Es gibt einen Trick, um Postgres zu verschieben, um einen Seqscan zu bevorzugen, der ein OFFSET 0
in der Unterabfrage hinzufügt
Dies ist praktisch, um Anforderungen zu optimieren, die große / große Tabellen verknüpfen, wenn Sie nur die n ersten / letzten Elemente benötigen.
Nehmen wir an, Sie suchen nach den ersten / letzten 20 Elementen mit mehreren Tabellen mit 100.000 (oder mehr) Einträgen. Es macht keinen Sinn, die gesamte Abfrage über alle Daten hinweg aufzubauen / zu verknüpfen, wenn das, wonach Sie suchen, in den ersten 100 oder 1000 liegt Einträge. In diesem Szenario ist es beispielsweise mehr als zehnmal schneller, einen sequentiellen Scan durchzuführen.
Siehe Wie kann ich verhindern, dass Postgres eine Unterabfrage einfügt?