Herauszufinden, warum eine Abfrage, die in der Entwicklung und beim Testen gut abschneidet, in der Produktion explodiert, kann manchmal eine Herausforderung sein. Lesen Sie weiter, um mehr über einige Funktionen zu erfahren, die Aufschluss darüber geben können, wie Ihre Abfragen in der Produktion abschneiden.
Derzeit laufende Abfragen
Wenn ein Client eine Verbindung zu einem PostgreSQL-Server herstellt, wird der Hauptprozess des Postgres-Servers (historisch postmaster genannt ) erzeugt einen neuen Prozess (das Backend genannt wird ), um die Anfragen des Kunden zu bedienen. Jedes Backend wartet daher entweder darauf, dass sein Client eine Abfrage sendet, oder versucht, eine auszuführen.
Die Systemansicht pg_stat_activity zeigt Informationen über jedes derzeit laufende Backend an. Insbesondere zeigt es die Abfrage an, die das Backend derzeit ausführt, wenn es aktiv ist, oder die letzte Abfrage, die es ausgeführt hat, wenn es darauf wartet, dass der Client eine weitere Abfrage sendet.
Hier sind zwei Backends, die Clients bedienen, die mit der Datenbank testdb
verbunden sind , wobei beide ihre Abfragen aktiv ausführen:
testdb=# select usename,datname,state,query from pg_stat_activity where datname='testdb';
-[ RECORD 1 ]-----------------------------------------------------------------------------
usename | postgres
datname | testdb
state | active
query | SELECT pg_sleep(10);
-[ RECORD 2 ]-----------------------------------------------------------------------------
usename | postgres
datname | testdb
state | active
query | select usename,datname,state,query from pg_stat_activity where datname='testdb';
Manchmal wartet die Abfrage möglicherweise auf eine Sperre, und auch dies zeigt inpg_stat_activity. Sie können hier ein INSERT sehen, das auf eine Beziehungssperre wartet:
testdb=# select wait_event_type, wait_event, left(query, 60) from pg_stat_activity where datname='testdb';
-[ RECORD 1 ]---+-------------------------------------------------------------
wait_event_type | Client
wait_event | ClientRead
left | lock table t in access exclusive mode;
-[ RECORD 2 ]---+-------------------------------------------------------------
wait_event_type |
wait_event |
left | select wait_event_type, wait_event, left(query, 60) from pg_
-[ RECORD 3 ]---+-------------------------------------------------------------
wait_event_type | Lock
wait_event | relation
left | insert into t values (1);
Weitere Informationen zu pg_stat_activity finden Sie in der Dokumentation.
Obwohl diese Ansicht hilfreich ist, um zu verstehen, was Postgres derzeit tut, bietet sie keine Informationen über Abfrageausführungsstatistiken oder Informationen über Abfragen, deren Ausführung abgeschlossen ist.
Alle Abfragen laufen in der Vergangenheit
Dafür die Erweiterung pg_stat_statements ist von unschätzbarem Wert. Diese Erweiterung ist in der zentralen PostgreSQL-Distribution enthalten und auch für verwaltete Dienste wie AWS RDS und GCP SQL verfügbar.
pg_stat_statements (PSS) ist eine „Erweiterung“ in PostgreSQL-Begriffen und muss zuerst installiert werden:
- Konsultieren Sie die Dokumentation Ihrer Linux-Distribution, um festzustellen, ob die Erweiterung vorinstalliert ist oder ob sie die Installation eines anderen Pakets erfordert. Auf Centos 7 müssen Sie beispielsweise
sudo yum install postgresql-contrib
ausführen . - Bearbeiten Sie die Hauptkonfigurationsdatei postgresql.conf (normalerweise unter
/etc
, wie/etc/postgresql/10/main/postgresql.conf
unter Debian) und ändern Sie den Wert vonshared_preload_libraries
zu „pg_stat_statements“. Dies ist eine durch Kommas getrennte Liste von Werten. Wenn dort also bereits etwas vorhanden ist, hängen Sie ein Komma an und dann „pg_stat_statements“. - Für AWS RDS müssen Sie Ihre aktive Parametergruppe ändern und den Wert festlegen.
- Nachdem Sie „shared_preload_libraries“ bearbeitet haben, müssen Sie den PostgreSQL-Daemon neu starten. Daran führt leider kein Weg vorbei. Auf AWS RDS müssen Sie die RDS-Instanz neu starten.
- Nach einem Neustart hätte der PostgreSQL-Server die gemeinsam genutzte Bibliothek geladen, und wir können die Erweiterung installieren, indem wir
CREATE EXTENSION pg_stat_statements
ausführen . Sie müssen ein Superuser sein, um diesen Befehl auszuführen. - Sie können die Erweiterung tatsächlich in jeder Datenbank installieren und dennoch die Abfragen in allen Datenbanken sehen.
Sobald die Erweiterung installiert ist, können Sie die Ansicht mit dem Namen pg_stat_statements
abfragen um Informationen über jede einzelne Abfrage zu erhalten, die seit der Installation der Erweiterung ausgeführt wurde.
Die Zahlen, wie die Zeit, die zum Ausführen der Abfrage benötigt wird, werden als Summe akkumuliert. Allein für die Abfrageausführungszeit werden einige Statistiken (Durchschnitt, Min., Max., Standardabweichung) präsentiert. Diese Werte können mit der Funktion pg_stat_statements_reset
gelöscht werden .
So sieht eine Zeile aus pg_stat_statements
aus sieht so aus:
testdb=# select * from pg_stat_statements where query like '%pg_sleep%' and dbid=42548;
-[ RECORD 1 ]-------+--------------------
userid | 10
dbid | 42548
queryid | 2649515222348904837
query | SELECT pg_sleep($1)
calls | 1
total_time | 10016.782625
min_time | 10016.782625
max_time | 10016.782625
mean_time | 10016.782625
stddev_time | 0
rows | 1
shared_blks_hit | 0
shared_blks_read | 0
shared_blks_dirtied | 0
shared_blks_written | 0
local_blks_hit | 0
local_blks_read | 0
local_blks_dirtied | 0
local_blks_written | 0
temp_blks_read | 0
temp_blks_written | 0
blk_read_time | 0
blk_write_time | 0
Abgesehen von den identifizierenden Parametern (Benutzer, Datenbank, Abfrage) können Sie viele interessante Dinge über Ihre Abfrage herausfinden:
- Wie lange die Ausführung normalerweise dauert (
mean_time
) - Wie viele Zeilen es im Durchschnitt zurückgibt (
rows
/calls
) - Die Menge der aus dem gemeinsam genutzten Puffercache gelesenen Daten und die Menge der von der Festplatte gelesenen Daten (der
shared_blks_read
zeigt die Gesamtmenge der Daten, die die Abfrage gelesen hat, davonshared_blks_hit
kam aus dem Cache) - Die Datenmenge, die aufgrund von Cache-Druck synchron auf die Festplatte geschrieben werden musste (
shared_blks_written
) - Die Menge der geschriebenen Daten als Anzahl der berührten Blöcke (
shared_blks_dirtied
) - Die für Lese- und Schreibvorgänge auf der Festplatte aufgewendete Zeit (
blk_{read,write}_time
) - Temporäre Dateien, in die geschrieben und von denen gelesen wird (
temp_blks_{read,written}
) - Temporäre Tabellen, in die geschrieben und aus denen gelesen wird (
local_*
)
Die Lese- und Schreibzeiten der Festplatte sind nur verfügbar, wenn der Konfigurationsparametertrack_io_timing
Ist eingeschaltet. Standardmäßig ist dies nicht der Fall. Auf den meisten modernen Linuxsystemen sollte es in Ordnung sein, diesen Parameter einzuschalten. Weiterlesen.
Es lohnt sich, die pg_stat_statements
zu schnappen Daten kontinuierlich in regelmäßigen Abständen, um zu sehen, wie sich diese Parameter pro Abfrage entwickeln. Das Open-Source-Tool pgmetrics kann die pg_stat_statements
extrahieren und verfügbar machen Daten als JSON für eine einfachere Automatisierung.
Während eines Zeitraums ausgeführte Abfragen
Sobald Sie ein solches System eingerichtet haben, ist es einfach, die in einem bestimmten Zeitrahmen ausgeführten Abfragen zu verfolgen. Dies erleichtert das Debuggen von Problemen, z. B. warum ein nächtlicher Batch-Job länger als erwartet gedauert hat.
Indem Sie die Zähler zwischen zwei gegebenen Zeitstempeln subtrahieren, können Sie wie zuvor die meisten Zahlen finden, mit Ausnahme von Minimum, Maximum und Standardabweichung. Dies reicht aus, um die Abfragen zu identifizieren, die innerhalb des Zeitbereichs ausgeführt wurden, und die Ressourcen, die sie verbraucht haben.
Langsame Abfragen protokollieren
Eine weitere Möglichkeit zum schnellen Identifizieren von Abfragen, die länger als erwartet dauern, besteht darin, die Protokollierung von Anweisungen zu aktivieren. Sie können eine Schwellendauer angeben, und wenn die Abfrage länger dauert, bis sie abgeschlossen ist, wird sie protokolliert. (In der regulären PostgreSQL-Protokolldatei gibt es keine separate für langsame Abfragen.)
Um diese Funktion zu aktivieren, bearbeiten Sie die Konfiguration wie folgt:
log_min_duration_statement = 1000 # in milliseconds
und Postgres neu laden. Sie können auch ALTER SYSTEM
verwenden :
ALTER SYSTEM SET log_min_duration_statement = 1000; -- in milliseconds
Damit wird jede Anweisung (einschließlich Nicht-DML-Anweisungen), deren Ausführung länger als eine Sekunde dauert, protokolliert:
2019-12-02 16:57:05.727 UTC [8040] postgres@testdb LOG: duration: 10017.862 ms statement: SELECT pg_sleep(10);
Die tatsächliche Zeit der Abfrage sowie der vollständige SQL-Text werden protokolliert.
Wenn Sie über ein Protokollüberwachungssystem verfügen und die Anzahl langsamer Abfragen pro Stunde / pro Tag verfolgen können, kann dies als guter Indikator für die Anwendungsleistung dienen.
Ausführungspläne für Abfragen
Sobald Sie eine Abfrage gefunden haben, die Ihrer Meinung nach schneller ausgeführt werden sollte, besteht der nächste Schritt darin, einen Blick auf ihren Abfrageplan zu werfen. In der Regel benötigen Sie den tatsächlichen Abfrageplan von Produktionsservern, um damit arbeiten zu können. Wenn Sie EXPLAIN so gut auf Produktionsservern ausführen können, müssen Sie sich ansonsten auf auto_explain
verlassen .
auto_explain
ist eine weitere zentrale PostgreSQL-Erweiterung, die entweder bereits installiert oder als „contrib“-Paket für Ihre Distribution verfügbar ist. Es ist auch auf AWSRDS verfügbar. auto_explain
ist etwas einfacher zu installieren als pg_stat_statements
:
- Bearbeiten Sie die Postgres-Konfiguration (oder die RDS-Parametergruppe)
shared_preload_libraries
umauto_explain
einzuschließen . - Sie müssen Postgres jedoch nicht neu starten, Sie können stattdessen einfach Folgendes ausführen:
LOAD 'auto_explain';
. - Du wirst seine Einstellungen konfigurieren wollen, zumindest diese hier:
auto_explain.log_min_duration = 1000 # seconds
Im Wesentlichen immer dann, wenn eine Abfrage länger als auto_explain.log_min_duration
dauert Anzahl der Sekunden bis zum Abschluss, auto_explainlogs die Abfrage und ihren Abfrageausführungsplan in der Protokolldatei wie folgt:
2019-12-04 09:23:05.130 UTC [12823] postgres@testdb LOG: duration: 11025.765 ms plan:
Query Text: select pg_sleep(11);
Result (cost=0.00..0.01 rows=1 width=4) (actual time=11025.716..11025.718 rows=1 loops=1)
Output: pg_sleep('11'::double precision)
Es kann den Plan auch im JSON-Format protokollieren, wenn Sie Skripts haben, die ihn verarbeiten können:
2019-12-02 17:30:53.676 UTC [8040] postgres@testdb LOG: duration: 10000.230 ms plan:
{
"Query Text": "SELECT pg_sleep(10);",
"Plan": {
"Node Type": "Result",
"Parallel Aware": false,
"Startup Cost": 0.00,
"Total Cost": 0.01,
"Plan Rows": 1,
"Plan Width": 4,
"Actual Startup Time": 10000.205,
"Actual Total Time": 10000.206,
"Actual Rows": 1,
"Actual Loops": 1,
"Output": ["pg_sleep('10'::double precision)"],
"Shared Hit Blocks": 0,
"Shared Read Blocks": 0,
"Shared Dirtied Blocks": 0,
"Shared Written Blocks": 0,
"Local Hit Blocks": 0,
"Local Read Blocks": 0,
"Local Dirtied Blocks": 0,
"Local Written Blocks": 0,
"Temp Read Blocks": 0,
"Temp Written Blocks": 0,
"I/O Read Time": 0.000,
"I/O Write Time": 0.000
},
"Triggers": [
]
}
In Postgres gibt es keine andere Möglichkeit als auto_explain, um sich den Ausführungsplan einer bereits ausgeführten Abfrage anzusehen, was auto_explain zu einem wichtigen Tool in Ihrer Toolbox macht.