Aggressivo Autovacuum su PostgreSQL


42

Sto cercando di far sì che PostgreSQL esegua l'aspirazione automatica del mio database. Al momento ho configurato il vuoto automatico come segue:

  • autovacuum_vacuum_cost_delay = 0 # Disattiva il vuoto basato sui costi
  • autovacuum_vacuum_cost_limit = 10000 # Valore massimo
  • autovacuum_vacuum_threshold = 50 # Valore predefinito
  • autovacuum_vacuum_scale_factor = 0.2 # Valore predefinito

Ho notato che il vuoto automatico si attiva solo quando il database non è sotto carico, quindi mi trovo in situazioni in cui ci sono molte più tuple morte che tuple vive. Vedi lo screenshot allegato per un esempio. Uno dei tavoli ha 23 tuple vive ma 16845 tuple morte in attesa di vuoto. È folle!

Un sacco di tuple morte

Il vuoto automatico interviene al termine dell'esecuzione del test e il server di database è inattivo, il che non è quello che desidero in quanto vorrei che il vuoto automatico entrasse in funzione ogni volta che il numero di tuple morte supera il 20% di tuple vive + 50, poiché il database è stato configurato. Il vuoto automatico quando il server è inattivo è inutile per me, poiché il server di produzione dovrebbe colpire migliaia di aggiornamenti / sec per un periodo prolungato, motivo per cui ho bisogno del vuoto automatico per funzionare anche quando il server è sotto carico.

C'è qualcosa che mi manca? Come forzare l'esecuzione del vuoto automatico mentre il server è sotto carico?

Aggiornare

Potrebbe essere un problema di blocco? Le tabelle in questione sono tabelle di riepilogo che vengono popolate tramite un trigger after insert. Queste tabelle sono bloccate in modalità SHARE ROW EXCLUSIVE per impedire scritture simultanee sulla stessa riga.

Risposte:


40

Eelke ha quasi sicuramente ragione che il tuo blocco sta bloccando il vuoto automatico. Autovacuum è progettato per lasciare spazio all'attività dell'utente, deliberatamente. Se quei tavoli sono bloccati, il vuoto automatico non può aspirarli.

Per i posteri, tuttavia, volevo fornire un esempio di impostazioni per l'autovacuum iper-aggressivo, dal momento che le impostazioni fornite non lo fanno del tutto. Tuttavia, è improbabile che rendere l'autovacuum più aggressivo risolva il problema. Si noti inoltre che le impostazioni di autovacuum predefinite si basano sull'esecuzione di oltre 200 esecuzioni di test utilizzando DBT2 che cerca una combinazione ottimale di impostazioni, quindi i valori predefiniti dovrebbero essere considerati buoni a meno che non si abbia un motivo valido per pensare diversamente, o a meno che il database non sia significativamente al di fuori il mainstream per i database OLTP (ad es. un minuscolo database che riceve aggiornamenti da 10 K al secondo o un data warehouse da 3 TB).

Innanzitutto, attiva la registrazione in modo da poter verificare se autovacuum sta facendo ciò che pensi che sia:

log_autovacuum_min_duration = 0

Quindi facciamo più lavoratori autovac e facciamo controllare più spesso le tabelle:

autovacuum_max_workers = 6
autovacuum_naptime = 15s

Abbassiamo le soglie per l'auto-vuoto e l'autoanalisi per innescare prima:

autovacuum_vacuum_threshold = 25
autovacuum_vacuum_scale_factor = 0.1

autovacuum_analyze_threshold = 10
autovacuum_analyze_scale_factor = 0.05 

Quindi rendiamo l'autovacuum meno interrompibile, quindi si completa più velocemente, ma a costo di avere un impatto maggiore sull'attività simultanea dell'utente:

autovacuum_vacuum_cost_delay = 10ms
autovacuum_vacuum_cost_limit = 1000

C'è il tuo programma completo per l'autovacuum genericamente aggressivo, che potrebbe essere appropriato per un piccolo database che ottiene una percentuale molto elevata di aggiornamenti, ma potrebbe avere un impatto troppo grande sull'attività simultanea dell'utente.

Inoltre, si noti che i parametri di autovacuum possono essere regolati per tabella , che è quasi sempre una risposta migliore per la necessità di regolare il comportamento di autovacuum.

Ancora una volta, tuttavia, è improbabile che affronti il ​​tuo vero problema.


36

Solo per vedere quali tabelle si qualificano per l'autovacuum, è possibile utilizzare la seguente query (basata su http://www.postgresql.org/docs/current/static/routine-vacuuming.html ). Si noti tuttavia che la query non cerca impostazioni specifiche della tabella:

 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;

11

Sì, è un problema di blocco. In base a questa pagina (non completo) VACUUM necessita dell'accesso ESCLUSIVO a SHARE UPDATE che è bloccato dal livello di blocco che si sta utilizzando.

Sei sicuro di aver bisogno di questo lucchetto? PostgreSQL è conforme all'ACID, quindi nella maggior parte dei casi le scritture simultanee non rappresentano un problema poiché PostgreSQL interromperà una delle transazioni in caso di violazione della serializzazione.

Inoltre, è possibile bloccare le righe utilizzando SELEZIONA PER AGGIORNARE per bloccare le righe anziché l'intera tabella.

Un'altra alternativa senza blocco sarebbe utilizzare il livello di isolamento della transazione serializzabile. Tuttavia, ciò potrebbe influire sulle prestazioni di altre transazioni e si dovrebbe essere preparati per ulteriori errori di serializzazione.


Ciò è dovuto al blocco delle tabelle di riepilogo, poiché queste sono bloccate utilizzando SHARE ROW EXCLUSIVE MODE. Le scritture simultanee senza il blocco potrebbero avere esito positivo, ma sicuramente finiranno con valori errati. Immagina di mantenere un conteggio N di righe di tipo X. Se inserisco contemporaneamente 2 righe di tipo X, senza blocco finirò con N + 1 nella mia tabella di riepilogo anziché N + 2. La soluzione che ho adottato è avere un lavoro cron che aspira manualmente le tabelle di riepilogo nel mio database. Funziona bene e sembra essere l'approccio consigliato, ma mi sembra troppo un hack.
CadentOrange

6

L'aumento del numero di processi di autovacuum e la riduzione del naptime probabilmente aiuteranno. Ecco la configurazione di PostgreSQL 9.1 che utilizzo su un server che memorizza le informazioni di backup e di conseguenza ottiene molte attività di inserimento.

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

Proverò anche ad abbassare cost_delayper rendere l'aspirazione più aggressiva.

Posso anche provare l'autovacuuming usando pgbench.

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

Esempio di contesa elevata:

Crea database Bench_replication

pgbench -i -p 5433 bench_replication

Esegui pgbench

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

Controlla lo stato dell'aspirazione automatica

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 | 

6

Lo script "qualify for autovacuum" esistente è molto utile, ma (come indicato correttamente) mancavano opzioni specifiche della tabella. Ecco una versione modificata di esso che tiene conto di tali opzioni:

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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.