Die Kosten von Postgres EXPLAIN verstehen
EXPLAIN
ist sehr nützlich, um die Leistung einer Postgres-Abfrage zu verstehen. Es gibt den vom PostgreSQL-Abfrageplaner für eine bestimmte Anweisung generierten Ausführungsplan zurück. Das EXPLAIN
Der Befehl gibt an, ob die Tabellen, auf die in einer Anweisung verwiesen wird, mit einem Indexscan oder einem sequentiellen Scan durchsucht werden.
Einige der ersten Dinge, die Sie bemerken werden, wenn Sie die Ausgabe von EXPLAIN
überprüfen Befehl sind die Kostenstatistiken, daher ist es natürlich, sich zu fragen, was sie bedeuten, wie sie berechnet und wie sie verwendet werden.
Kurz gesagt, der PostgreSQL-Abfrageplaner schätzt, wie viel Zeit die Abfrage in Anspruch nehmen wird (in einer beliebigen Einheit), sowohl mit den Startkosten als auch mit den Gesamtkosten für jeden Vorgang. Dazu später mehr. Wenn es mehrere Optionen zum Ausführen einer Abfrage hat, verwendet es diese Kosten, um die günstigste und damit hoffentlich schnellste Option zu wählen.
In welcher Einheit sind die Kosten?
Die Kosten sind in einer beliebigen Einheit angegeben. Ein häufiges Missverständnis ist, dass sie in Millisekunden oder einer anderen Zeiteinheit angegeben werden, aber das ist nicht der Fall.
Die Kosteneinheiten sind (standardmäßig) an einer einzigen sequenziellen gelesenen Seite verankert, die 1,0 Einheiten kostet (seq_page_cost
). Jede verarbeitete Zeile fügt 0,01 hinzu (cpu_tuple_cost
), und jede nicht sequenzielle gelesene Seite fügt 4,0 (random_page_cost
) hinzu ). Es gibt viele weitere Konstanten wie diese, die alle konfigurierbar sind. Letzteres ist ein besonders häufiger Kandidat, zumindest auf moderner Hardware. Wir werden uns das gleich genauer ansehen.
Startkosten
Die ersten Zahlen, die Sie nach cost=
sehen werden als „Anlaufkosten“ bezeichnet. Dies ist eine Schätzung, wie lange es dauern wird, die erste Zeile abzurufen . Daher beinhalten die Startkosten einer Operation die Kosten ihrer Kinder.
Bei einem sequentiellen Scan liegen die Startkosten im Allgemeinen nahe bei Null, da sofort mit dem Abrufen von Zeilen begonnen werden kann. Bei einer Sortieroperation ist sie höher, da ein großer Teil der Arbeit erledigt werden muss, bevor Zeilen zurückgegeben werden können.
Um ein Beispiel zu sehen, erstellen wir eine einfache Testtabelle mit 1000 Benutzernamen:
CREATE TABLE users (id bigint GENERIERT IMMER ALS IDENTITÄT PRIMÄRSCHLÜSSEL, username text NOT NULL);INSERT INTO users (username)SELECT 'person' || nFROM generate_series(1, 1000) AS n;Benutzer analysieren;
Werfen wir einen Blick auf einen einfachen Abfrageplan mit einigen Operationen:
EXPLAIN SELECT * FROM Benutzer ORDER BY Benutzername;QUERY PLAN |------------------------------ ----------------------+Sortieren (Kosten=66,83..69,33 Zeilen=1000 Breite=17) | Sortierschlüssel:Benutzername | -> Seq Scan bei Benutzern (Kosten=0,00..17,00 Zeilen=1000 Breite=17)|
Im obigen Abfrageplan, wie erwartet, die geschätzten Ausführungskosten der Anweisung für den Seq Scan
ist 0.00
, und für die Sort
ist 66.83
.
Gesamtkosten
Die zweite Kostenstatistik nach den Anlaufkosten und den zwei Punkten wird als „Gesamtkosten“ bezeichnet. Dies ist eine Schätzung, wie lange es dauern wird, alle Zeilen zurückzugeben .
Sehen wir uns diesen beispielhaften Abfrageplan noch einmal an:
ABFRAGEPLAN |------------------------------------------- ------------------+Sortieren (Kosten=66,83..69,33 Zeilen=1000 Breite=17) | Sortierschlüssel:Benutzername | -> Seq Scan bei Benutzern (Kosten=0,00..17,00 Zeilen=1000 Breite=17)|
Wir können sehen, dass die Gesamtkosten des Seq Scan
Betriebszeit ist 17.00
. Für die Sort
Der Betrieb beträgt 69,33, was nicht viel mehr ist als die Startkosten (wie erwartet).
Die Gesamtkosten umfassen in der Regel die Kosten der vorangegangenen Operationen. Beispielsweise enthalten die Gesamtkosten des obigen Sortiervorgangs die des Seq-Scans.
Eine wichtige Ausnahme ist LIMIT
Klauseln, anhand derer der Planer abschätzt, ob er vorzeitig abbrechen kann. Wenn nur eine kleine Anzahl von Zeilen benötigt wird, deren Bedingungen üblich sind, kann berechnet werden, dass eine einfachere Scan-Auswahl billiger ist (wahrscheinlich schneller).
Zum Beispiel:
EXPLAIN SELECT * VON Benutzern LIMIT 1;ABFRAGEPLAN |------------------------------------ --------------------------+Limit (Kosten=0,00..0,02 Zeilen=1 Breite=17) | -> Seq Scan bei Benutzern (Kosten=0,00..17,00 Zeilen=1000 Breite=17)|
Wie Sie sehen können, betragen die auf dem Seq Scan-Knoten gemeldeten Gesamtkosten immer noch 17,00, aber die vollen Kosten der Limit-Operation werden mit 0,02 gemeldet. Dies liegt daran, dass der Planer davon ausgeht, dass er nur 1 Zeile von 1000 verarbeiten muss, sodass die Kosten in diesem Fall auf 1000stel der Gesamtkosten geschätzt werden.
Wie die Kosten berechnet werden
Um diese Kosten zu berechnen, verwendet der Abfrageplaner von Postgres sowohl Konstanten (von denen wir einige bereits gesehen haben) als auch Metadaten über den Inhalt der Datenbank. Die Metadaten werden oft als „Statistiken“ bezeichnet.
Statistiken werden über ANALYZE
gesammelt (Nicht zu verwechseln mit EXPLAIN
gleichnamiger Parameter) und in pg_statistic
gespeichert . Sie werden auch automatisch als Teil der Selbstbereinigung aktualisiert.
Diese Statistiken enthalten eine Reihe sehr nützlicher Dinge, wie ungefähr die Anzahl der Zeilen, die jede Tabelle hat, und was die häufigsten Werte in jeder Spalte sind.
Sehen wir uns ein einfaches Beispiel an, bei dem dieselben Abfragedaten wie zuvor verwendet werden:
EXPLAIN SELECT count(*) FROM users;QUERY PLAN |------------------------------ --------------------------+Aggregat (Kosten=19,50..19,51 Zeilen=1 Breite=8) | -> Seq Scan auf Benutzer (Kosten=0.00..17.00 Zeilen=1000 Breite=0)|
In unserem Fall deuteten die Statistiken des Planers darauf hin, dass die Daten für die Tabelle innerhalb von 7 Seiten (oder Blöcken) gespeichert wurden und dass 1000 Zeilen zurückgegeben würden. Die Kostenparameter seq_page_cost
, cpu_tuple_cost
und cpu_operator_cost
wurden auf ihren Standardwerten von 1
belassen , 0.01
und 0.0025
bzw..
Daher wurden die Gesamtkosten des Seq-Scans wie folgt berechnet:
Gesamtkosten von Seq Scan=(geschätzte sequenzielle Seitenaufrufe * seq_page_cost) + (geschätzte zurückgegebene Zeilen * cpu_tuple_cost)=(7 * 1) + (1000 * 0,01)=7 + 10,00=17,00
Und für das Aggregat als:
Gesamtkosten von Aggregate=(Kosten von Seq Scan) + (geschätzte verarbeitete Zeilen * cpu_operator_cost) + (geschätzte zurückgegebene Zeilen * cpu_tuple_cost)=(17,00) + (1000 * 0,0025) + (1 * 0,01) =17,00 + 2,50 + 0,01 =19,51
Wie der Planer die Kosten verwendet
Da wir wissen, dass Postgres den Abfrageplan mit den niedrigsten Gesamtkosten auswählt, können wir damit versuchen, die getroffenen Entscheidungen zu verstehen. Wenn eine Abfrage beispielsweise nicht den erwarteten Index verwendet, können Sie Einstellungen wie enable_seqscan
verwenden um von bestimmten Abfrageplanoptionen massiv abzuraten. An diesem Punkt sollten Sie nicht überrascht sein zu hören, dass Einstellungen wie diese zu einer Erhöhung der Kosten führen!
Zeilennummern sind ein äußerst wichtiger Teil der Kostenschätzung. Sie werden verwendet, um Schätzungen für verschiedene Join-Reihenfolgen, Join-Algorithmen, Scan-Typen und mehr zu berechnen. Zeilenkostenschätzungen, die um ein Vielfaches abweichen, können dazu führen, dass die Kostenschätzung um ein Vielfaches verfehlt wird, was letztendlich dazu führen kann, dass eine suboptimale Planauswahl getroffen wird.
Mit EXPLAIN ANALYZE einen Abfrageplan erhalten
Wenn Sie SQL-Anweisungen in PostgreSQL schreiben, wird die Datei ANALYZE
Der Befehl ist der Schlüssel zur Optimierung von Abfragen, wodurch sie schneller und effizienter werden. Zusätzlich zur Anzeige des Abfrageplans und der PostgreSQL-Schätzungen bietet die EXPLAIN ANALYZE
Option führt die Abfrage durch (Vorsicht bei UPDATE
und DELETE
!) und zeigt die tatsächliche Ausführungszeit und Zeilenanzahl für jeden Schritt im Ausführungsprozess an. Dies ist für die Überwachung der SQL-Leistung erforderlich.
Sie können EXPLAIN ANALYZE
verwenden um die geschätzte Anzahl von Zeilen mit den tatsächlichen Zeilen zu vergleichen, die von jeder Operation zurückgegeben werden.
Sehen wir uns ein Beispiel an, wobei wir wieder dieselben Daten verwenden:
ABFRAGEPLAN |------------------------------------------- -------------------------------------------------- -------------+Sortieren (Kosten=66,83..69,33 Zeilen=1000 Breite=17) (tatsächliche Zeit=20.569..20.684 Zeilen=1000 Schleifen=1) | Sortierschlüssel:Benutzername | Sortiermethode:Quicksort Speicher:102kB | -> Seq Scan auf Benutzer (Kosten =0,00..17,00 Zeilen =1000 Breite =17) (tatsächliche Zeit =0,048..0,596 Zeilen =1000 Schleifen =1)|Planungszeit:0,171 ms |Ausführungszeit:20,793 ms |Wir können sehen, dass die Gesamtausführungskosten immer noch 69,33 betragen, wobei der Großteil davon auf die Sortieroperation entfällt und 17,00 auf den sequenziellen Scan entfallen. Beachten Sie, dass die Abfrageausführungszeit knapp unter 21 ms liegt.
Sequenzieller Scan vs. Index-Scan
Lassen Sie uns nun einen Index hinzufügen, um zu versuchen, diese kostspielige Art der gesamten Tabelle zu vermeiden:
CREATE INDEX people_username_idx ON users (Benutzername);EXPLAIN ANALYZE SELECT * FROM users ORDER BY Benutzername;QUERY PLAN |---------------------- -------------------------------------------------- -------------------------------------------------- ------+Index-Scan mit people_username_idx für Benutzer (Kosten=0,28..28,27 Zeilen=1000 Breite=17) (tatsächliche Zeit=0,052..1,494 Zeilen=1000 Schleifen=1)|Planungszeit:0,186 ms |Ausführung Zeit:1,686 ms |Wie Sie sehen, hat sich der Abfrageplaner jetzt für einen Index-Scan entschieden, da die Gesamtkosten dieses Plans 28,27 (weniger als 69,33) betragen. Es scheint, dass der Index-Scan effizienter war als der sequentielle Scan, da die Abfrageausführungszeit jetzt knapp unter 2 ms liegt.
Hilf dem Planer, genauer zu schätzen
Wir können dem Planer auf zwei Arten helfen, genauer zu schätzen:
- Hilf ihm dabei, bessere Statistiken zu erstellen
- Passen Sie die für die Berechnungen verwendeten Konstanten an
Die Statistik kann nach einer großen Änderung der Daten in einer Tabelle besonders schlecht sein. Wenn Sie also viele Daten in eine Tabelle laden, können Sie Postgres helfen, indem Sie eine manuelle
ANALYZE
ausführen darauf. Statistiken bleiben auch bei einem größeren Versions-Upgrade nicht erhalten, also ist dies ein weiterer wichtiger Zeitpunkt dafür.Natürlich ändern sich Tabellen auch im Laufe der Zeit, daher kann es sehr hilfreich sein, die Autovacuum-Einstellungen so anzupassen, dass sie häufig genug für Ihre Arbeitslast ausgeführt werden.
Wenn Sie Probleme mit schlechten Schätzungen für eine Spalte mit einer schiefen Verteilung haben, können Sie davon profitieren, die Menge an Informationen zu erhöhen, die Postgres sammelt, indem Sie
ALTER TABLE SET STATISTICS
verwenden Befehl oder sogar dasdefault_statistics_target
für die gesamte Datenbank.Eine weitere häufige Ursache für schlechte Schätzungen ist, dass Postgres standardmäßig davon ausgeht, dass zwei Spalten unabhängig sind. Sie können dies beheben, indem Sie es bitten, Korrelationsdaten für zwei Spalten aus derselben Tabelle über erweiterte Statistiken zu sammeln.
An der Constant-Tuning-Front gibt es viele Parameter, die Sie an Ihre Hardware anpassen können. Angenommen, Sie arbeiten mit SSDs, möchten Sie wahrscheinlich zumindest Ihre Einstellung von
random_page_cost
optimieren . Dies ist standardmäßig 4, was 4x teurer ist als dieseq_page_cost
wir haben uns vorhin angesehen. Dieses Verhältnis war auf sich drehenden Festplatten sinnvoll, aber auf SSDs neigt es dazu, zufällige E/A zu sehr zu benachteiligen. Daher könnte eine Einstellung näher an 1 oder zwischen 1 und 2 sinnvoller sein. Bei ScaleGrid verwenden wir standardmäßig 1.Kann ich die Kosten aus Abfrageplänen entfernen?
Aus vielen der oben genannten Gründe lassen die meisten Leute die Kosten beim Ausführen von
EXPLAIN
stehen . Wenn Sie möchten, können Sie sie jedoch mitCOSTS
ausschalten Parameter.ERKLÄREN (KOSTEN AUS) AUSWÄHLEN * VON Benutzern LIMIT 1;ABFRAGEPLAN |----------------------+Limit | -> Seq Scan bei Benutzern|Schlussfolgerung
Um es noch einmal zusammenzufassen:Die Kosten in Abfrageplänen sind Schätzungen von Postgres für die Dauer einer SQL-Abfrage in einer beliebigen Einheit.
Es wählt den Plan mit den niedrigsten Gesamtkosten aus, basierend auf einigen konfigurierbaren Konstanten und gesammelten Statistiken.
Es ist sehr wichtig, ihm dabei zu helfen, diese Kosten genauer zu schätzen, damit es gute Entscheidungen treffen und Ihre Abfragen leistungsfähig halten kann.
|