Wir überwachen die PostgreSQL-Datenbank - wer ist schuld und was zu tun ist

Ich habe bereits darüber gesprochen, wie wir PostgreSQL-Probleme mithilfe der Massenprotokollüberwachung auf Hunderten von Servern gleichzeitig „abfangen“ . Neben den Protokollen bietet uns dieses DBMS auch viele Tools zur Analyse seines Status - es ist eine Sünde, sie nicht zu verwenden.

Wenn Sie sie nur von der Konsole aus betrachten, können Sie sehr schnell und ohne Nutzen herumlaufen, da die uns zur Verfügung stehende Datenmenge alle angemessenen Grenzen überschreitet.


Damit die Situation kontrollierbar bleibt, haben wir für Zabbix ein Add-On entwickelt , das Metriken bereitstellt , Bildschirme erstellt und einheitliche Überwachungsregeln für alle Server und Datenbanken auf diesen erstellt.

In dem heutigen Artikel geht es darum, welche Schlussfolgerungen durch dynamische Beobachtung der verschiedenen Metriken der PostgreSQL-Serverbasen gezogen werden können und wo das Problem möglicherweise verborgen ist.

Verbindungsstatus


Das allererste, womit alle Demontagen zum Thema „Was ist mit der Datenbank passiert / es war schlecht“ beginnen, ist die Überwachung des Zusammenfassungsstatus von pg_stat_activity :



In der linken Grafik sehen wir alle Verbindungen, die auf etwas warten, auf der rechten Seite - die etwas sind tun. Abhängig von der PG-Version wird der Verbindungsstatus durch pg_stat_activity.state/wait_eventund / oder den Text der Anforderung selbst bestimmt.

Worauf zu achten ist :

  • Zu wenigidle - Irgendwann sind in Ihrer Anwendung möglicherweise nicht genügend Verbindungen zur Datenbank geöffnet. Wenn Sie versuchen, eine andere zu öffnen, warten Sie darauf, dass der Prozess initialisiert wird, um eine neue Verbindung herzustellen.
  • idle «» , max_connections.
  • idle in transaction — , - pgbouncer. .

    , , idle_in_transaction_session_timeout.
  • wait — - «» . — .

    , «» pg_terminate_backend(pid).
  • active ( max-) , «». - (, « ») , , …

    — , «» .
  • maintenance — , - :

    query ~* E'^(\\s*(--[^\\n]*\\n|/\\*.*\\*/|\\n))*(autovacuum|VACUUM|ANALYZE|REINDEX|CLUSTER|CREATE|ALTER|TRUNCATE|DROP)'

    In den meisten Fällen wird gleichzeitig die Anzahl der automatischen Vakuum- / Autoanalysen ausgeführt, deren Schaden nur darin besteht, Serverressourcen für "fremde" Fälle zu verwenden. Wenn dies für Sie kritisch ist - drehen autovacuum_max_workersund autovacuum_naptime, aber vollständig ausschalten - sollten Sie dies nicht tun .

    Wenn jedoch gleichzeitig zu wachsen beginnt waitundmaintenance es eine Chance ist, zu sehen, ob sich jemand entschlossen hat, den DBA- oder Entwicklercode einzuführen, um beispielsweise die Hälfte der Wahrscheinlichkeit funktionsfähiger Anwendungen zu blockieren.

Da es für uns wichtig ist, nicht nur viele Metriken zu entfernen, sondern dies auch so effizient wie möglich zu tun, versuchen wir, einige davon im Rahmen einer Anfrage synchron aufzunehmen:

Verbindungs- und Sperrstatus
WITH event_types(wait_event_type) AS(
  VALUES
    ('lwlock')
  , ('lock')
  , ('bufferpin')
  , ('client')
  , ('extension')
  , ('ipc')
  , ('timeout')
  , ('io')
)
, events(wait_event) AS(
  VALUES
    ('walwritelock')
  , ('wal_insert')
  , ('buffer_content')
  , ('buffer_io')
  , ('lock_manager')
  , ('relation')
  , ('extend')
  , ('page')
  , ('tuple')
  , ('transactionid')
  , ('virtualxid')
  , ('speculative token')
  , ('object')
  , ('userlock')
  , ('advisory')
  , ('clientread')
  , ('datafileextend')
  , ('datafileread')
  , ('datafilewrite')
  , ('slruread')
  , ('slruwrite')
)
, states(state) AS(
  VALUES
    ('running')
  , ('maintenance')
  , ('waiting')
  , ('transaction')
  , ('idle')
)
, stats AS(
  SELECT
    pid
  , datname
  , state
  , lower(wait_event_type) wait_event_type
  , lower(wait_event) wait_event
  , query
  FROM
    pg_stat_activity
  WHERE
    pid <> pg_backend_pid()
)
, dbs AS(
  SELECT
    datname
  FROM
    pg_database db
  WHERE
    NOT db.datistemplate
)
  SELECT
    date_part('epoch', now())::integer ts
  , coalesce(s.qty, 0) val
  , dbs.datname dbname
  , states.state
  , true total
  FROM
    dbs
  CROSS JOIN
    states
  NATURAL LEFT JOIN
    (
      SELECT
        datname
      , CASE
          WHEN query ~* E'^(\\s*(--[^\\n]*\\n|/\\*.*\\*/|\\n))*(autovacuum|VACUUM|ANALYZE|REINDEX|CLUSTER|CREATE|ALTER|TRUNCATE|DROP)' THEN
            'maintenance'
          WHEN wait_event IS NOT NULL AND
            wait_event <> 'clientread' AND
            state = 'active' THEN
            'waiting'
          WHEN state = 'active' THEN
            'running'
          WHEN state = 'idle' THEN
            'idle'
          WHEN state IN ('idle in transaction', 'idle in transaction (aborted)') THEN
            'transaction'
          WHEN state = 'fastpath function call' THEN
            'fastpath'
          ELSE
            'disabled'
        END state
      , count(*) qty
      FROM
        stats
      GROUP BY
        1, 2
    ) s
UNION
  SELECT
    date_part('epoch', now())::integer ts
  , coalesce(t.qty, 0) val
  , dbs.datname dbname
  , event_types.wait_event_type
  , false total
  FROM
    dbs
  CROSS JOIN
    event_types
  NATURAL LEFT JOIN
    (
      SELECT
        datname
      , wait_event_type
      , count(*) qty
      FROM
        stats
      WHERE
        wait_event_type IS NOT NULL
      GROUP BY
        1, 2
    ) t
UNION
  SELECT
    date_part('epoch', now())::integer ts
  , coalesce(e.qty, 0) val
  , dbs.datname dbname
  , events.wait_event
  , false total
  FROM
    dbs
  CROSS JOIN
    events
  NATURAL LEFT JOIN
    (
      SELECT
        datname
      , wait_event
      , count(*) qty
      FROM
        stats
      WHERE
        wait_event IS NOT NULL
      GROUP BY
        1, 2
    ) e;

Schlösser


Da wir im vorherigen Absatz auf die Überwachung von Sperren eingegangen sind, ist es erwähnenswert, dass PostgreSQL sie gerne links und rechts anwendet:



Von diesen interessieren uns zwei Arten am meisten:

  • Exclusive - tritt normalerweise auf, wenn ein bestimmter Datensatz gesperrt wird.
  • AccessExclusive - bei Wartungsarbeiten am Tisch.

Vergessen Sie jedoch nicht, dass die Gesamtzahl der Schlösser nicht aus Gummi besteht :
Sowohl empfohlene als auch reguläre Sperren werden im gemeinsam genutzten Speicherbereich gespeichert, dessen Größe durch die Konfigurationsparameter max_locks_per_transactionund bestimmt wird max_connections. Es ist wichtig, dass dieser Speicher ausreichend ist, da der Server sonst keine Sperre ausstellen kann . Daher ist die Anzahl der empfohlenen Sperren, die ein Server ausgeben kann, je nach Serverkonfiguration normalerweise auf Zehntausende oder Hunderttausende begrenzt.
In der Regel tritt diese Situation auf, wenn Ihre Anwendung „fließt“ und Ressourcen nicht freigegeben werden: Verbindungen zur Datenbank, Transaktionskontexte oder Beratungssperren . Achten Sie daher auf die Gesamtdynamik.

Transaktionen pro Sekunde (TPS)


Um Informationen zu Änderungen im Kontext der aktuellen Datenbank abzurufen , können Sie die Systemansicht pg_stat_database verwenden . Wenn sich jedoch viele Datenbanken auf dem Server befinden, ist es praktisch, dies sofort für alle zu tun und eine Verbindung zu herzustellenpostgres .

TPS & Tupel
SELECT
  extract(epoch from now())::integer ts
, datname dbname
, pg_stat_get_db_tuples_returned(oid) tup_returned
, pg_stat_get_db_tuples_fetched(oid) tup_fetched
, pg_stat_get_db_tuples_inserted(oid) tup_inserted
, pg_stat_get_db_tuples_updated(oid) tup_updated
, pg_stat_get_db_tuples_deleted(oid) tup_deleted
, pg_stat_get_db_xact_commit(oid) xact_commit
, pg_stat_get_db_xact_rollback(oid) xact_rollback
FROM
  pg_database
WHERE
  NOT datistemplate;

Ich möchte separat hervorheben - vernachlässigen Sie nicht die Ausgabe von Maximalwerten von Metriken!



In dieser Grafik sehen wir deutlich die Situation eines plötzlichen Spitzenanstiegs der Anzahl der durchgeführten ( commit) Transaktionen. Dies ist kein Einzelgespräch, das der Auslastung des Servers entspricht, und Transaktionen können unterschiedlich komplex sein. Ein vierfaches Wachstum zeigt jedoch deutlich, dass der Server über eine bestimmte Leistungsreserve verfügen sollte, um einen solchen Spitzenwert problemlos zu überstehen.

Nun, das Rollback ( rollback) der Transaktion ist eine Gelegenheit, um zu überprüfen, ob Ihre Anwendung bewusst ausgeführt ROLLBACKwird oder ob der Server dies aufgrund eines Fehlers automatisch tut.

Anzahl der Operationen an Datensätzen


Achten Sie zunächst auf die Datensätze, die wir von Indizes / Tabellen abziehen:



  • tuples.returned — , «» .
  • tuples.fetched — , « » Rows Removed by Filter, «» .
  • tuples.ratio — , , 1, — . , , , .

Wenn Sie einen scharfen Peak beobachtentuples.ratio , können Sie sicher sein, dass eine ineffiziente Anfrage aus der im Artikel beschriebenen Kategorie zu Rezepten für deren Behandlung im Protokoll auf Sie zukommt .

Allerdings, auch wenn im ratioIdealfall gleich 1, aber der Peak fielreturned/fetched - auch nicht gut erwarten. Normalerweise kann dies bedeuten, dass der Plan irgendwelche Probleme enthält, wie zum Beispiel:

Hash Join
  - Hash
    - Seq Scan on BIG_TABLE
  - Index Scan ...

Merge Join
  - Index Scan on BIG_INDEX
  - Index Scan ...

Da wir zu überprüfen begonnen haben, was dort gelesen wird, wollen wir sehen, wie es passiert. Das heißt, wie viele Datensätze wir von Indizes lesen und wie viel als Ergebnis Seq Scan:



Es ist klar, dass hier ein ungeplanter Anstieg der Indikatoren Verdacht erregen sollte. Wenn Sie beispielsweise aus irgendeinem Grund jede Nacht eine ganze Platte mit 10 Millionen Datensätzen lesen müssen, ist das Auftreten eines solchen Peaks während des Tages ein Grund für die Demontage.

Sowie alle massenanomalen Einfügungen / Aktualisierungen / Löschungen:



Datencache verwenden


Um zu verstehen, wie das Massen-Korrekturlesen von Datensätzen die Lebensdauer des Servers wirklich verschlechtert, schauen wir uns die Arbeit des Servers mit Datenseiten und das Verhältnis anblock.read/hit . In einer idealen Welt sollte der Server shared readabsolut nichts von der Festplatte ( auf dem Plan-Knoten) „lesen“ , alles sollte sich bereits im Speicher befinden ( shared hit), da der Zugriff auf die Festplatte immer langsam ist .

In Wirklichkeit ist dies nicht ganz richtig und der Grund für eine gründliche Analyse der Anfragen um die Spitzenzeit:



Längste Anfrage / Transaktion


Für MVCC sind lang laufende Abfragen und Transaktionen in ausgelasteten Systemen eine Leistungskatastrophe. Details und Bilder dazu finden Sie hier und hier - wie können Sie unter solchen Bedingungen noch überleben?



Solche Bösewichte zu fangen hilft uns pg_stat_activity.query_start/xact_start.

Wie unsere Erfahrung zeigt, reicht eine visuelle Darstellung dieser Metriken bereits aus, um grob darzustellen, wo weiter "gegraben" werden muss:

  • Suchen Sie in der Anwendung nach Ressourcenlecks
  • Fehlgeschlagene Anforderungen optimieren
  • Setzen Sie produktivere Hardware
  • ... oder stellen Sie sicher, dass die Last rechtzeitig richtig verteilt ist

All Articles