Die klassische Frage, mit der ein Entwickler zu seinem DBA oder dem GeschĂ€ftsinhaber â zum PostgreSQL-Berater â kommt, klingt fast immer gleich: âWarum dauern die Abfragen auf der Datenbank so lange?â
Der traditionelle Satz von GrĂŒnden:
- ineffizienter Algorithmus
wenn Sie beschlossen haben, mehrere CTE ĂŒber ein paar Zehntausend DatensĂ€tze zu verknĂŒpfen - nicht aktuelle Statistiken.
wenn die tatsÀchliche Verteilung der Daten in der Tabelle bereits stark von der zuletzt durch ANALYZE gesammelten abweicht - ein Ressourcenengpass.
und die zugewiesenen CPU-Rechenressourcen nicht ausreichen, stĂ€ndig werden Gigabyte an Arbeitsspeicher benötigt oder die Festplatte kommt mit all den âWĂŒnschenâ der DB nicht nach - von Sperren von konkurrierenden Prozessen
Und wenn die Sperren ziemlich schwer zu erfassen und zu analysieren sind, dann genĂŒgt uns fĂŒr alles andere der Abfrageplan, den man mit Hilfe von (am besten, gleich EXPLAIN (ANALYZE, BUFFERS) âŠ) oder .
Aber, wie in derselben Dokumentation gesagt,
âDas VerstĂ€ndnis des Plans ist eine Kunst, und um es zu beherrschen, benötigt man gewisse Erfahrung, âŠâ
Aber man kann auch ohne ihn auskommen, wenn man das richtige Werkzeug nutzt!
Wie sieht ein Abfrageplan normalerweise aus? So etwa:
Index Scan using pg_class_relname_nsp_index on pg_class (actual time=0.049..0.050 rows=1 loops=1)
Index Cond: (relname = $1)
Filter: (oid = $0)
Buffers: shared hit=4
InitPlan 1 (returns $0,$1)
-> Limit (actual time=0.019..0.020 rows=1 loops=1)
Buffers: shared hit=1
-> Seq Scan on pg_class pg_class_1 (actual time=0.015..0.015 rows=1 loops=1)
Filter: (relkind = 'r'::"char")
Rows Removed by Filter: 5
Buffers: shared hit=1oder so:
"Append (cost=868.60..878.95 rows=2 width=233) (actual time=0.024..0.144 rows=2 loops=1)"
" Buffers: shared hit=3"
" CTE cl"
" -> Seq Scan on pg_class (cost=0.00..868.60 rows=9972 width=537) (actual time=0.016..0.042 rows=101 loops=1)"
" Buffers: shared hit=3"
" -> Limit (cost=0.00..0.10 rows=1 width=233) (actual time=0.023..0.024 rows=1 loops=1)"
" Buffers: shared hit=1"
" -> CTE Scan on cl (cost=0.00..997.20 rows=9972 width=233) (actual time=0.021..0.021 rows=1 loops=1)"
" Buffers: shared hit=1"
" -> Limit (cost=10.00..10.10 rows=1 width=233) (actual time=0.117..0.118 rows=1 loops=1)"
" Buffers: shared hit=2"
" -> CTE Scan on cl cl_1 (cost=0.00..997.20 rows=9972 width=233) (actual time=0.001..0.104 rows=101 loops=1)"
" Buffers: shared hit=2"
"Planning Time: 0.634 ms"
"Execution Time: 0.248 ms"Aber einen Plan âvom Blattâ zu lesen, ist sehr schwierig und unĂŒbersichtlich:
- im Knoten wird die Summe der Ressourcen des Teilbaums
Das heiĂt, um zu verstehen, wie viel Zeit fĂŒr die AusfĂŒhrung eines bestimmten Knotens benötigt wurde, oder wie viele Daten genau bei diesem Lesevorgang aus der Tabelle vom DatentrĂ€ger geholt wurden â muss man irgendwie eines vom anderen abziehen - die Zeit des Knotens muss mit loops multipliziert werden
Ja, Subtraktion ist noch nicht die schwierigste Operation, die man "im Kopf" machen muss â denn die AusfĂŒhrungszeit wird als Durchschnitt fĂŒr eine AusfĂŒhrung eines Knotens angegeben, und davon kann es Hunderte geben. - Nun, und all das zusammen erschwert es, die Hauptfrage zu beantworten â wer ist also derjenige? "Das schwĂ€chste Glied"?
Als wir versuchten, all dies einer mehreren Hundert unserer Entwickler zu erklĂ€ren, merkten wir, dass es von auĂen ungefĂ€hr so aussieht:

Also, brauchen wirâŠ
Tool
In ihm haben wir versucht, alle SchlĂŒsselmechanismen zusammenzustellen, die helfen, gemÀà dem Plan und der Anfrage zu verstehen, "wer schuld ist und was zu tun ist". Nun, und einen Teil unserer Erfahrungen mit der Gemeinschaft zu teilen.
Willkommen und nutzen Sie es â
Anschaulichkeit der PlÀne
Ist es leicht, den Plan zu verstehen, wenn er so aussieht?
Seq Scan on pg_class (tatsÀchliche Zeit=0.009..1.304 Zeilen=6609 Schleifen=1)
Buffers: shared hit=263
Planungszeit: 0.108 ms
AusfĂŒhrungszeit: 1.800 ms
Nicht wirklich.
Aber so, in verkĂŒrzter Form, wenn die SchlĂŒsselindikatoren getrennt sind â ist es bereits viel verstĂ€ndlicher:

Aber wenn der Plan komplizierter ist â hilft uns das Tortendiagramm der Zeitverteilung nach Knoten:

Nun, und fĂŒr die kompliziertesten Varianten eilt zur Hilfe das AusfĂŒhrungsdiagramm:

Zum Beispiel gibt es durchaus nicht triviale Situationen, in denen ein Plan mehr als einen tatsÀchlichen Wurzelknoten haben kann:


Strukturelle Hinweise
Nun, und wenn die gesamte Struktur des Plans und seine Schwachstellen bereits offen gelegt und sichtbar sind â warum nicht dem Entwickler hervorheben und in "normaler Sprache" erklĂ€ren?
Solche Empfehlungsvorlagen haben wir bereits ein paar Dutzend gesammelt.
Zeilenweiser Profiling des Antrags
Jetzt, wenn wir den analysierten Plan mit dem ursprĂŒnglichen Antrag ĂŒberlagern, können wir sehen, wie viel Zeit fĂŒr jeden einzelnen Operator aufgewendet wurde â ungefĂ€hr so:

⊠oder sogar so:

Parameterersetzung im Antrag
Wenn Sie den Antrag nicht nur mit dem Plan, sondern auch mit seinen Parametern aus der DETAIL-Zeile des Protokolls verknĂŒpft haben, können Sie ihn zusĂ€tzlich in einer der Varianten kopieren:
- mit Ersetzung der Werte im Antrag
fĂŒr die direkte AusfĂŒhrung in Ihrer Datenbank und anschlieĂendes ProfilingSELECT 'const', 'param'::text; - mit Ersetzung der Werte ĂŒber PREPARE/EXECUTE
zum Emulieren der Planerarbeit, wenn die parametrische Komponente ignoriert werden kann â zum Beispiel bei der Arbeit mit partitionierten TabellenDEALLOCATE ALL; PREPARE q(text) AS SELECT 'const', $1::text; EXECUTE q('param'::text);
Archiv der PlÀne
FĂŒgen Sie ein, analysieren Sie und teilen Sie mit Ihren Kollegen! Die PlĂ€ne bleiben im Archiv, und Sie können spĂ€ter darauf zurĂŒckkommen:
Wenn Sie jedoch nicht möchten, dass andere Ihren Plan sehen, vergessen Sie nicht, das KĂ€stchen ânicht im Archiv veröffentlichenâ anzukreuzen.
In den nĂ€chsten Artikeln werde ich ĂŒber die Schwierigkeiten und Lösungen sprechen, die beim Analysieren von PlĂ€nen auftreten.
Quelle: habr.com
