Aggressives Autovakuum unter PostgreSQL

42

Ich versuche, PostgreSQL dazu zu bringen, meine Datenbank aggressiv automatisch zu vakuumieren. Ich habe das automatische Vakuum derzeit wie folgt konfiguriert:

  • autovacuum_vacuum_cost_delay = 0 #Kostenbasiertes Vakuum ausschalten
  • autovacuum_vacuum_cost_limit = 10000 #Max value
  • autovacuum_vacuum_threshold = 50 # Standardwert
  • autovacuum_vacuum_scale_factor = 0.2 # Standardwert

Ich stelle fest, dass das automatische Vakuum nur aktiviert wird, wenn die Datenbank nicht geladen ist, sodass ich Situationen erlebe, in denen es weit mehr tote Tupel als lebende Tupel gibt. Ein Beispiel finden Sie im beigefügten Screenshot. Einer der Tische hat 23 lebende Tupel, aber 16845 tote Tupel warten auf Vakuum. Das ist verrückt!

Viele tote Tupel

Auto Vacuum wird aktiviert, wenn der Testlauf beendet ist und der Datenbankserver inaktiv ist. Dies ist nicht das, was ich möchte, da Auto Vacuum aktiviert werden soll, wenn die Anzahl der toten Tupel 20% der aktiven Tupel + 50 überschreitet, wie es die Datenbank getan hat konfiguriert. Das automatische Saugen im Leerlauf des Servers ist für mich unbrauchbar, da der Produktionsserver voraussichtlich über einen längeren Zeitraum hinweg 1000 Updates / Sek. Erreicht. Aus diesem Grund muss das automatische Saugen auch dann ausgeführt werden, wenn der Server ausgelastet ist.

Fehlt mir etwas? Wie kann ich das automatische Staubsaugen erzwingen, während der Server stark ausgelastet ist?

Aktualisieren

Könnte dies ein Sperrproblem sein? Bei den fraglichen Tabellen handelt es sich um Übersichtstabellen, die über einen After-Insert-Trigger aufgefüllt werden. Diese Tabellen sind im SHARE ROW EXCLUSIVE-Modus gesperrt, um gleichzeitiges Schreiben in dieselbe Zeile zu verhindern.

CadentOrange
quelle

Antworten:

40

Eelke hat mit ziemlicher Sicherheit Recht, dass Ihre Verriegelung das automatische Vakuum blockiert. Autovacuum ist so konzipiert, dass es absichtlich Benutzeraktivitäten nachgibt. Wenn diese Tische gesperrt sind, kann das automatische Absaugen sie nicht absaugen.

Für die Nachwelt wollte ich jedoch ein Beispiel für einen Satz von Einstellungen für hyperaggressives Autovakuum geben, da die von Ihnen angegebenen Einstellungen dies nicht ganz tun. Beachten Sie jedoch, dass es unwahrscheinlich ist, dass Sie Ihr Problem lösen, wenn Sie das automatische Vakuum aggressiver machen. Beachten Sie außerdem, dass die standardmäßigen Einstellungen für das automatische Vakuum auf der Ausführung von über 200 Testläufen mit DBT2 basieren, bei denen nach einer optimalen Kombination von Einstellungen gesucht wird. Daher sollte davon ausgegangen werden, dass die Standardeinstellungen gut sind, es sei denn, Sie haben einen stichhaltigen Grund, etwas anderes zu denken, oder Ihre Datenbank befindet sich erheblich außerhalb des zulässigen Bereichs der Mainstream für OLTP-Datenbanken (z. B. eine winzige Datenbank, die 10 KB Updates pro Sekunde erhält, oder ein 3 TB Data Warehouse).

Aktivieren Sie zuerst die Protokollierung, damit Sie überprüfen können, ob Autovacuum das tut, was Sie denken, dass es ist:

log_autovacuum_min_duration = 0

Dann lassen Sie uns mehr Autovac-Mitarbeiter beschäftigen und die Tabellen öfter überprüfen:

autovacuum_max_workers = 6
autovacuum_naptime = 15s

Senken Sie die Schwellenwerte für das automatische Vakuum und die automatische Analyse, um sie früher auszulösen:

autovacuum_vacuum_threshold = 25
autovacuum_vacuum_scale_factor = 0.1

autovacuum_analyze_threshold = 10
autovacuum_analyze_scale_factor = 0.05 

Dann machen wir das automatische Vakuum weniger unterbrechbar, damit es schneller abgeschlossen wird, aber auf Kosten einer größeren Auswirkung auf die gleichzeitige Benutzeraktivität:

autovacuum_vacuum_cost_delay = 10ms
autovacuum_vacuum_cost_limit = 1000

Es gibt ein vollständiges Programm für generisch aggressives Autovakuum, das für eine kleine Datenbank geeignet ist, die eine sehr hohe Aktualisierungsrate aufweist, sich jedoch zu stark auf die gleichzeitige Benutzeraktivität auswirkt.

Beachten Sie auch, dass die Autovakuum-Parameter pro Tabelle angepasst werden können. Dies ist fast immer eine bessere Antwort, wenn das Verhalten des Autovakuums angepasst werden muss.

Auch hier ist es unwahrscheinlich, dass Sie Ihr eigentliches Problem angehen.

Josh Berkus
quelle
36

Um festzustellen, welche Tabellen überhaupt für Autovakuum geeignet sind, kann die folgende Abfrage verwendet werden (basierend auf http://www.postgresql.org/docs/current/static/routine-vacuuming.html ). Beachten Sie jedoch, dass die Abfrage nicht nach tabellenspezifischen Einstellungen sucht:

 SELECT psut.relname,
     to_char(psut.last_vacuum, 'YYYY-MM-DD HH24:MI') as last_vacuum,
     to_char(psut.last_autovacuum, 'YYYY-MM-DD HH24:MI') as last_autovacuum,
     to_char(pg_class.reltuples, '9G999G999G999') AS n_tup,
     to_char(psut.n_dead_tup, '9G999G999G999') AS dead_tup,
     to_char(CAST(current_setting('autovacuum_vacuum_threshold') AS bigint)
         + (CAST(current_setting('autovacuum_vacuum_scale_factor') AS numeric)
            * pg_class.reltuples), '9G999G999G999') AS av_threshold,
     CASE
         WHEN CAST(current_setting('autovacuum_vacuum_threshold') AS bigint)
             + (CAST(current_setting('autovacuum_vacuum_scale_factor') AS numeric)
                * pg_class.reltuples) < psut.n_dead_tup
         THEN '*'
         ELSE ''
     END AS expect_av
 FROM pg_stat_user_tables psut
     JOIN pg_class on psut.relid = pg_class.oid
 ORDER BY 1;
Pygrac
quelle
11

Ja, es handelt sich um ein Sperrproblem. Laut dieser Seite (nicht voll) benötigt VACUUM SHARE UPDATE EXCLUSIVE-Zugriff, der durch die von Ihnen verwendete Sperrstufe blockiert wird.

Bist du sicher, dass du dieses Schloss brauchst? PostgreSQL ist ACID-konform, daher sind gleichzeitige Schreibvorgänge in den meisten Fällen kein Problem, da PostgreSQL eine der Transaktionen abbricht, wenn eine Serialisierungsverletzung auftritt.

Sie können Zeilen auch sperren, indem Sie SELECT FOR UPDATE verwenden , um Zeilen anstelle der gesamten Tabelle zu sperren.

Eine andere Alternative wäre ohne Verriegelung , die Transaktion mit Isolationsstufe serializable. Dies kann sich jedoch auf die Leistung anderer Transaktionen auswirken, und Sie sollten auf weitere Serialisierungsfehler vorbereitet sein.

Eelke
quelle
Dies ist auf das Sperren der Übersichtstabellen zurückzuführen, da diese mit SHARE ROW EXCLUSIVE MODE gesperrt werden. Gleichzeitige Schreibvorgänge ohne die Sperre sind möglicherweise erfolgreich, führen jedoch mit Sicherheit zu falschen Werten. Stellen Sie sich vor, ich halte eine Anzahl N von Zeilen des Typs X aufrecht. Wenn ich gleichzeitig 2 Zeilen des Typs X einfüge, ohne zu sperren, werde ich in meiner Übersichtstabelle N + 1 anstelle von N + 2 erhalten. Die von mir angenommene Lösung soll einen Cron-Job haben, der die Übersichtstabellen in meiner Datenbank manuell staubsaugt. Es funktioniert gut und scheint der empfohlene Ansatz zu sein, aber es fühlt sich zu sehr nach einem Hack für mich an.
CadentOrange
6

Das Erhöhen der Anzahl der Autovakuum-Prozesse und das Verringern der Naptime werden wahrscheinlich helfen. Hier ist die Konfiguration für ein PostgreSQL 9.1, das ich auf einem Server verwende, der Sicherungsinformationen speichert und infolgedessen viel Einfügeaktivität erhält.

http://www.postgresql.org/docs/current/static/runtime-config-autovacuum.html

autovacuum_max_workers = 6              # max number of autovacuum subprocesses
autovacuum_naptime = 10         # time between autovacuum runs
autovacuum_vacuum_cost_delay = 20ms     # default vacuum cost delay for

Ich werde auch versuchen, die cost_delayzu senken , um das Staubsaugen aggressiver zu machen.

Ich kann das automatische Staubsaugen auch mit pgbench testen.

http://wiki.postgresql.org/wiki/Pgbenchtesting

Beispiel für eine hohe Konkurrenz:

Erstellen Sie die bench_replication-Datenbank

pgbench -i -p 5433 bench_replication

Führen Sie pgbench aus

pgbench -U postgres -p 5432 -c 64 -j 4 -T 600 bench_replication

Überprüfen Sie den automatischen Staubsaugerstatus

psql
>\connect bench_replicaiton
bench_replication=# select schemaname, relname, last_autovacuum from pg_stat_user_tables;
 schemaname |     relname      |        last_autovacuum        
------------+------------------+-------------------------------
 public     | pgbench_branches | 2012-07-18 18:15:34.494932+02
 public     | pgbench_history  | 
 public     | pgbench_tellers  | 2012-07-18 18:14:06.526437+02
 public     | pgbench_accounts | 
Craig Efrein
quelle
6

Vorhandenes "Qualify for Autovacuum" -Skript ist sehr nützlich, aber (wie korrekt angegeben) fehlten tabellenspezifische Optionen. Hier ist eine modifizierte Version davon, die diese Optionen berücksichtigt:

WITH rel_set AS
(
    SELECT
        oid,
        CASE split_part(split_part(array_to_string(reloptions, ','), 'autovacuum_vacuum_threshold=', 2), ',', 1)
            WHEN '' THEN NULL
        ELSE split_part(split_part(array_to_string(reloptions, ','), 'autovacuum_vacuum_threshold=', 2), ',', 1)::BIGINT
        END AS rel_av_vac_threshold,
        CASE split_part(split_part(array_to_string(reloptions, ','), 'autovacuum_vacuum_scale_factor=', 2), ',', 1)
            WHEN '' THEN NULL
        ELSE split_part(split_part(array_to_string(reloptions, ','), 'autovacuum_vacuum_scale_factor=', 2), ',', 1)::NUMERIC
        END AS rel_av_vac_scale_factor
    FROM pg_class
) 
SELECT
    PSUT.relname,
    to_char(PSUT.last_vacuum, 'YYYY-MM-DD HH24:MI')     AS last_vacuum,
    to_char(PSUT.last_autovacuum, 'YYYY-MM-DD HH24:MI') AS last_autovacuum,
    to_char(C.reltuples, '9G999G999G999')               AS n_tup,
    to_char(PSUT.n_dead_tup, '9G999G999G999')           AS dead_tup,
    to_char(coalesce(RS.rel_av_vac_threshold, current_setting('autovacuum_vacuum_threshold')::BIGINT) + coalesce(RS.rel_av_vac_scale_factor, current_setting('autovacuum_vacuum_scale_factor')::NUMERIC) * C.reltuples, '9G999G999G999') AS av_threshold,
    CASE
        WHEN (coalesce(RS.rel_av_vac_threshold, current_setting('autovacuum_vacuum_threshold')::BIGINT) + coalesce(RS.rel_av_vac_scale_factor, current_setting('autovacuum_vacuum_scale_factor')::NUMERIC) * C.reltuples) < PSUT.n_dead_tup
        THEN '*'
    ELSE ''
    END AS expect_av
FROM
    pg_stat_user_tables PSUT
    JOIN pg_class C
        ON PSUT.relid = C.oid
    JOIN rel_set RS
        ON PSUT.relid = RS.oid
ORDER BY C.reltuples DESC;
Vadim Zingertal
quelle