PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Wenn Autovacuum nicht saugt

Vor ein paar Wochen habe ich die Grundlagen des Autovakuum-Tunings erklärt. Am Ende dieses Beitrags habe ich versprochen, mich bald mit Problemen mit dem Staubsaugen zu befassen. Nun, es hat etwas länger gedauert als geplant, aber los geht's.

Um es kurz zusammenzufassen:autovacuum ist ein Hintergrundprozess, der tote Zeilen bereinigt, z. alte gelöschte Zeilenversionen. Sie können die Bereinigung auch manuell durchführen, indem Sie VACUUM ausführen , aber autovacuum macht das automatisch, abhängig von der Anzahl toter Zeilen in der Tabelle, im richtigen Moment – ​​nicht zu oft, aber oft genug, um die Menge an „Müll“ unter Kontrolle zu halten.

Allgemein gesagt, autovacuum kann nicht zu oft ausgeführt werden – die Bereinigung wird erst durchgeführt, nachdem eine bestimmte Anzahl toter Zeilen in der Tabelle angesammelt wurde. Es kann sich jedoch aus verschiedenen Gründen verzögern, was dazu führt, dass Tabellen und Indizes größer als erwünscht werden. Und genau das ist das Thema dieses Beitrags. Was sind also die häufigsten Übeltäter und wie kann man sie identifizieren?

Drosselung

Wie in Tuning-Grundlagen erklärt, autovacuum Arbeiter werden gedrosselt, um nur eine bestimmte Menge an Arbeit pro Zeitintervall auszuführen. Die Standardgrenzen sind ziemlich niedrig – etwa 4 MB/s beim Schreiben, 8 MB/s beim Lesen. Das ist für winzige Maschinen wie Raspberry Pi oder kleine Server von vor 10 Jahren geeignet, aber aktuelle Maschinen sind viel leistungsfähiger (sowohl in Bezug auf CPU als auch E/A) und verarbeiten viel mehr Daten.

Stellen Sie sich vor, Sie haben ein paar große Tabellen und einige kleine. Wenn alle drei autovacuum Die Arbeiter fangen an, die großen Tische aufzuräumen, keiner der kleinen Tische wird gesaugt, unabhängig von der Menge an toten Reihen, die sich ansammeln. Dies zu erkennen ist nicht besonders schwierig, vorausgesetzt, Sie haben eine ausreichende Überwachung. Suchen Sie nach Zeiträumen, in denen alle autovacuum sind Arbeiter sind beschäftigt, während Tische nicht gesaugt werden, obwohl sich viele tote Reihen ansammeln.

Alle notwendigen Informationen befinden sich in pg_stat_activity (Anzahl autovacuum Arbeitsprozesse) und pg_stat_all_tables (last_autovacuum und n_dead_tup ).

Erhöhen der Anzahl von autovacuum Arbeitnehmer ist keine Lösung, da die Gesamtarbeitsmenge gleich bleibt. Sie können Drosselungslimits pro Tabelle angeben und diesen Worker aus dem Gesamtlimit ausschließen, aber das garantiert immer noch nicht, dass bei Bedarf Worker verfügbar sind.

Die richtige Lösung besteht darin, die Drosselung zu optimieren, wobei Grenzen verwendet werden, die in Bezug auf die Hardwarekonfiguration und die Arbeitslastmuster angemessen sind. Einige grundlegende Drosselungsempfehlungen wurden im vorherigen Beitrag erwähnt. (Natürlich wäre dies eine ideale Lösung, wenn Sie die Menge der in der Datenbank generierten toten Zeilen reduzieren könnten.)

Ab diesem Punkt gehen wir davon aus, dass die Drosselung nicht das Problem ist, d. h. dass das autovacuum Arbeiter nicht für längere Zeit ausgelastet sind und dass die Bereinigung auf allen Tischen ohne unangemessene Verzögerungen ausgelöst wird.

Lange Transaktionen

Wenn der Tisch also regelmäßig gesaugt wird, können sich sicher nicht viele tote Reihen ansammeln, oder? Unglücklicherweise nicht. Die Zeilen können nicht unmittelbar nach dem Löschen „entfernt“ werden, sondern nur, wenn es keine Transaktionen gibt, die sie möglicherweise sehen könnten. Das genaue Verhalten hängt davon ab, was die anderen Transaktionen tun (machen) und von der Serialisierungsebene, aber im Allgemeinen:

LESEN VERPFLICHTET

  • Ausführen von Abfragen blockiert die Bereinigung
  • leere Transaktionen blockieren die Bereinigung nur, wenn sie einen Schreibvorgang ausgeführt haben
  • leere Transaktionen (ohne Schreibvorgänge) blockieren die Bereinigung nicht (aber es ist sowieso keine gute Praxis, sie zu behalten)

SERIALISIERBAR

  • Ausführen von Abfragen blockiert die Bereinigung
  • leere Transaktionen blockieren die Bereinigung (selbst wenn sie nur gelesen haben)

In der Praxis ist es natürlich nuancierter, aber um all die verschiedenen Bits zu erklären, müsste man zuerst erklären, wie XIDs und Snapshots funktionieren, und das ist nicht das Ziel dieses Beitrags. Was Sie daraus wirklich mitnehmen sollten, ist, dass lange Transaktionen eine schlechte Idee sind, insbesondere wenn diese Transaktionen möglicherweise Schreibvorgänge ausgeführt haben.

Natürlich gibt es durchaus triftige Gründe, warum Sie Transaktionen für längere Zeit aufbewahren müssen (z. B. wenn Sie ACID für alle Änderungen sicherstellen müssen). Achten Sie aber darauf, dass es nicht unnötig passiert, z.B. aufgrund eines schlechten Anwendungsdesigns.

Eine etwas unerwartete Folge davon ist eine hohe CPU- und E/A-Auslastung aufgrund von autovacuum immer wieder laufen, ohne irgendwelche toten Zeilen (oder nur ein paar von ihnen) zu reinigen. Aus diesem Grund können die Tische in der nächsten Runde immer noch aufgeräumt werden, was mehr schadet als nützt.

Wie erkennt man das? Erstens müssen Sie lang andauernde Transaktionen überwachen, insbesondere inaktive. Sie müssen lediglich Daten aus pg_stat_activity lesen . Die Ansichtsdefinition ändert sich ein wenig mit der PostgreSQL-Version, daher müssen Sie dies möglicherweise ein wenig optimieren:

SELECT xact_start, state FROM pg_stat_activity;

-- count 'idle' transactions longer than 15 minutes (since BEGIN)
SELECT COUNT(*) FROM pg_stat_activity
 WHERE state = 'idle in transaction'
  AND (now() - xact_start) > interval '15 minutes'

-- count transactions 'idle' for more than 5 minutes
SELECT COUNT(*) FROM pg_stat_activity
 WHERE state = 'idle in transaction'
  AND (now() - state_change) > interval '5 minutes'

Sie können auch einfach ein vorhandenes Überwachungs-Plugin verwenden, z. check_postgres.pl. Diese beinhalten bereits diese Art von Plausibilitätsprüfungen. Sie müssen entscheiden, was eine angemessene Transaktions-/Abfragedauer ist, die anwendungsspezifisch ist.

Seit PostgreSQL 9.6 können Sie auch idle_in_transaction_session_timeout verwenden damit Transaktionen, die zu lange inaktiv sind, automatisch beendet werden. Ebenso gibt es für lange Abfragen statement_timeout .

Eine weitere nützliche Sache ist VACUUM VERBOSE was Ihnen tatsächlich sagt, wie viele tote Zeilen noch nicht entfernt werden konnten:

db=# VACUUM verbose z;
INFO:  vacuuming "public.z"
INFO:  "z": found 0 removable, 66797 nonremovable row versions in 443 out of 443 pages
DETAIL:  12308 dead row versions cannot be removed yet.
...

Es wird Ihnen nicht sagen, welches Backend die Bereinigung verhindert, aber es ist ein ziemlich klares Zeichen dafür, was passiert.

Hinweis: . Sie können diese Informationen nicht einfach von autovacuum erhalten weil es nur mit DEBUG2 protokolliert wird standardmäßig (und Sie möchten sicherlich nicht mit dieser Protokollebene in der Produktion laufen).

Lange Abfragen zu Hot-Standbys

Nehmen wir an, dass Tabellen zeitnah evakuiert werden, aber keine toten Tupel entfernen, was zu einer Aufblähung von Tabellen und Indizes führt. Sie überwachen pg_stat_activity und es gibt keine lang andauernden Transaktionen. Was könnte das Problem sein?

Wenn Sie eine Streaming-Replik haben, besteht die Möglichkeit, dass das Problem dort liegt. Wenn das Replikat hot_standby_feedback=on verwendet , verhalten sich Abfragen auf dem Replikat ziemlich genau wie Transaktionen auf dem primären, einschließlich der Blockierung der Bereinigung. Natürlich hot_standby_feedback=on wird genau dann verwendet, wenn lange Abfragen (z. B. Analysen und BI-Arbeitslasten) auf Replikaten ausgeführt werden, um Abbrüche aufgrund von Replikationskonflikten zu verhindern.

Leider müssen Sie sich entscheiden – entweder hot_standby_feedback=on beibehalten und akzeptieren Sie Verzögerungen bei der Bereinigung oder behandeln Sie abgebrochene Abfragen. Sie können auch max_standby_streaming_delay verwenden um die Auswirkungen zu begrenzen, obwohl dies die Abbrüche nicht vollständig verhindert (also müssen Sie die Abfragen trotzdem wiederholen).

Tatsächlich gibt es jetzt eine dritte Option – die logische Replikation. Anstatt die physische Streaming-Replikation für das BI-Replikat zu verwenden, können Sie die Änderungen mithilfe der neuen logischen Replikation kopieren, die in PostgreSQL 10 verfügbar ist. Die logische Replikation lockert die Kopplung zwischen dem primären und dem Replikat und macht die Cluster größtenteils unabhängig (werden unabhängig bereinigt, usw.).

Dies löst die beiden Probleme im Zusammenhang mit der physischen Streaming-Replikation – verzögerte Bereinigung bei primären oder abgebrochenen Abfragen auf dem BI-Replikat. Für Replikate, die DR-Zwecken dienen, bleibt die Streaming-Replikation jedoch die richtige Wahl. Aber diese Replikate führen keine langen Abfragen aus (oder sollten dies nicht tun).

Hinweis: Während ich erwähnte, dass die logische Replikation in PostgreSQL 10 verfügbar sein wird, war ein erheblicher Teil der Infrastruktur in früheren Versionen (insbesondere PostgreSQL 9.6) verfügbar. Sie können dies also möglicherweise sogar auf älteren Versionen tun (wir haben das für einige unserer Kunden getan), aber PostgreSQL 10 wird es viel bequemer und komfortabler machen.

Problem mit autoanalyze

Ein Detail, das Sie vielleicht vermissen, ist das autovacuum Arbeiter führen eigentlich zwei verschiedene Aufgaben aus. Zuerst die Bereinigung (als ob VACUUM ausgeführt würde ), sondern auch das Sammeln von Statistiken (als ob ANALYZE ausgeführt würde ). Und beides Teile werden mit autovacuum_cost_limit gedrosselt .

Aber es gibt einen großen Unterschied bei der Abwicklung von Transaktionen. Immer wenn das VACUUM Teil erreicht autovacuum_cost_limit , gibt der Worker den Snapshot frei und schläft eine Weile. Die ANALYZE muss jedoch in einem einzigen Snapshot/einer einzigen Transaktion ausgeführt werden, was tatsächlich ist Bereinigung blockieren.

Dies ist eine elegante Art, sich selbst ins Knie zu schießen, besonders wenn Sie auch einiges davon tun:

  • erhöhen Sie default_statistics_target um genauere Statistiken aus größeren Stichproben zu erstellen
  • verringern Sie autovacuum_analyze_scale_factor um häufiger Statistiken zu sammeln

Die unbeabsichtigte Folge ist natürlich, dass ANALYZE wird häufiger vorkommen, viel länger dauern und wird (im Gegensatz zum VACUUM Teil) verhindern die Bereinigung. Die Lösung ist normalerweise ziemlich einfach – verringern Sie nicht autovacuum_analyze_scale_factor zu viel. Ausführen von ANALYZE 10 % der Tabellenänderungen sollten in den meisten Fällen mehr als genug sein.

n_dead_tup

Eine letzte Sache, die ich erwähnen möchte, betrifft Änderungen in pg_stat_all_tables.n_dead_tup Werte. Sie könnten denken, dass der Wert ein einfacher Zähler ist, der erhöht wird, wenn ein neues totes Tupel erstellt wird, und verringert wird, wenn es bereinigt wird. Aber es ist eigentlich nur eine Schätzung der Anzahl toter Tupel, aktualisiert von ANALYZE . Bei kleinen Tabellen (weniger als 240 MB) ist das kein großer Unterschied, da ANALYZE liest die ganze Tabelle und ist daher ziemlich genau. Bei großen Tabellen kann es sich jedoch ziemlich ändern, je nachdem, welche Teilmenge der Tabelle abgetastet wird. Und das Verringern von autovacuum_vacuum_scale_factor macht es zufälliger.

Seien Sie also vorsichtig, wenn Sie sich n_dead_tup ansehen in einem Überwachungssystem. Plötzliches Abfallen oder Ansteigen des Werts kann einfach auf ANALYZE zurückzuführen sein Neuberechnung einer anderen Schätzung, und nicht aufgrund einer tatsächlichen Bereinigung und/oder neuer toter Tupel, die in der Tabelle erscheinen.

Zusammenfassung

Um dies in ein paar einfachen Punkten zusammenzufassen:

  • autovacuum kann nur funktionieren, wenn es keine Transaktionen gibt, die die toten Tupel benötigen könnten.
  • Lange laufende Abfragen blockieren die Bereinigung. Erwägen Sie die Verwendung von statement_timeout um den Schaden zu begrenzen.
  • Lang andauernde Transaktionen können die Bereinigung blockieren. Das genaue Verhalten hängt von Dingen wie der Isolationsstufe oder dem, was in der Transaktion passiert ist, ab. Überwachen Sie sie und beenden Sie sie, wenn möglich.
  • Lang andauernde Abfragen auf Replikate mit hot_standby_feedback=on kann auch die Bereinigung blockieren.
  • autoanalyze wird ebenfalls gedrosselt, aber im Gegensatz zum VACUUM Teil behält es einen einzigen Snapshot (und blockiert somit die Bereinigung).
  • n_dead_tup ist nur eine Schätzung, die von ANALYZE verwaltet wird , erwarten Sie also einige Schwankungen (insbesondere bei großen Tabellen).