J'essaie d'obtenir PostgreSQL pour nettoyer automatiquement et agressivement ma base de données. J'ai actuellement configuré le vide automatique comme suit:
Je remarque que le vide automatique ne se déclenche que lorsque la base de données n'est pas sous charge, donc je me retrouve dans des situations où il y a beaucoup plus de tuples morts que de tuples vivants. Voir la capture d'écran ci-jointe pour un exemple. L'une des tables contient 23 tuples vivants mais 16845 tuples morts en attente de vide. C'est dingue!
L'aspiration automatique se déclenche lorsque le test se termine et que le serveur de base de données est inactif, ce qui n'est pas ce que je veux car j'aimerais que l'aspiration automatique se déclenche chaque fois que le nombre de tuples morts dépasse 20% de tuples vivants + 50, car la base de données a été configuré. L'aspiration automatique lorsque le serveur est inactif est inutile pour moi, car le serveur de production devrait atteindre des milliers de mises à jour/s pendant une période prolongée, c'est pourquoi j'ai besoin de l'aspiration automatique pour fonctionner même lorsque le serveur est en charge.
Y a-t-il quelque chose qui me manque? Comment forcer l'exécution de l'aspirateur automatique lorsque le serveur est soumis à une forte charge?
Mise à jour
Serait-ce un problème de verrouillage? Les tableaux en question sont des tableaux récapitulatifs qui sont remplis via un déclencheur après insertion. Ces tables sont verrouillées en mode SHARE ROW EXCLUSIVE pour empêcher les écritures simultanées sur la même ligne.
Eelke a presque certainement raison de dire que votre verrouillage bloque le vide automatique. Autovacuum est conçu pour céder délibérément la place à l'activité de l'utilisateur. Si ces tables sont verrouillées, l'autovacuum ne peut pas les aspirer.
Pour la postérité, cependant, je voulais donner un exemple d'ensemble de paramètres pour un autovacuum hyper-agressif, car les paramètres que vous avez donnés ne le font pas tout à fait. Notez cependant que rendre l'auto-vide plus agressif ne résoudra probablement pas votre problème. Notez également que les paramètres de vide automatique par défaut sont basés sur l'exécution de plus de 200 tests en utilisant DBT2 à la recherche d'une combinaison optimale de paramètres, de sorte que les valeurs par défaut doivent être considérées comme bonnes, sauf si vous avez une bonne raison de penser le contraire, ou si votre base de données est nettement en dehors le courant dominant pour les bases de données OLTP (par exemple, une petite base de données qui reçoit 10 000 mises à jour par seconde, ou un entrepôt de données de 3 To).
Tout d'abord, activez la journalisation pour vérifier si l'autovacuum fait ce que vous pensez qu'il est:
log_autovacuum_min_duration = 0
Faisons alors plus de travailleurs autovac et faisons-leur vérifier les tables plus souvent:
autovacuum_max_workers = 6
autovacuum_naptime = 15s
Abaissons les seuils d'auto-vide et d'auto-analyse pour qu'ils se déclenchent plus tôt:
autovacuum_vacuum_threshold = 25
autovacuum_vacuum_scale_factor = 0.1
autovacuum_analyze_threshold = 10
autovacuum_analyze_scale_factor = 0.05
Rendons ensuite l'autovacuum moins interruptible, afin qu'il se termine plus rapidement, mais au prix d'avoir un impact plus important sur l'activité simultanée des utilisateurs:
autovacuum_vacuum_cost_delay = 10ms
autovacuum_vacuum_cost_limit = 1000
Il existe votre programme complet pour un vide automatique générique agressif, qui pourrait convenir à une petite base de données obtenant un taux de mises à jour très élevé, mais qui pourrait avoir un impact trop important sur l'activité simultanée des utilisateurs.
Notez également que les paramètres du vide automatique peuvent être ajustés par table , ce qui est presque toujours une meilleure réponse pour avoir besoin d'ajuster le comportement du vide automatique .
Encore une fois, cependant, il est peu probable qu'il résout votre problème réel.
Juste pour voir quelles tables sont éligibles pour le vide automatique, la requête suivante peut être utilisée (basée sur http://www.postgresql.org/docs/current/static/routine-vacuuming.html ). Notez cependant que la requête ne recherche pas les paramètres spécifiques à la table:
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;
Oui, c'est un problème de verrouillage. En fonction de cela page (non plein) VACUUM a besoin d'un accès EXCLUSIF SHARE UPDATE qui est bloqué par le niveau de verrouillage que vous utilisez.
Êtes-vous certain d'avoir besoin de ce verrou? PostgreSQL est conforme à ACID, donc les écritures simultanées ne sont dans la plupart des cas pas un problème car PostgreSQL annulera l'une des transactions en cas de violation de sérialisation.
Vous pouvez également verrouiller des lignes en utilisant SELECT FOR UPDATE pour verrouiller les lignes au lieu de la table entière.
Une autre alternative sans verrouillage serait d'utiliser la transaction niveau d'isolement sérialisable. Cependant, cela peut avoir un impact sur les performances d'autres transactions et vous devez être prêt à affronter davantage d'échecs de sérialisation.
Augmenter le nombre de processus de vide automatique et réduire la sieste aidera probablement. Voici la configuration d'un PostgreSQL 9.1 que j'utilise sur un serveur qui stocke des informations de sauvegarde et qui, par conséquent, reçoit beaucoup d'activité d'insertion.
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
Je vais également essayer de réduire le cost_delay
pour rendre l'aspirateur plus agressif.
Je peux également tester l'auto-vide en utilisant pgbench.
http://wiki.postgresql.org/wiki/Pgbenchtesting
Exemple de conflit élevé:
Créer une base de données bench_replication
pgbench -i -p 5433 bench_replication
Exécutez pgbench
pgbench -U postgres -p 5432 -c 64 -j 4 -T 600 bench_replication
Vérifier l'état de la purge automatique
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 |
Le script "qualifier pour autovacuum" est très utile, mais (comme indiqué correctement) il manquait des options spécifiques à la table. En voici une version modifiée qui tient compte de ces options:
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;