TL;DR — Resumo Rápido
Domine o ajuste de VACUUM e autovacuum no PostgreSQL para eliminar bloat, prevenir o wraparound de XID e maximizar o desempenho do seu banco de dados.
O VACUUM no PostgreSQL é uma das operações de manutenção mais incompreendidas e ao mesmo tempo mais críticas em qualquer implantação PostgreSQL. Sem ajuste adequado, as tabelas acumulam silenciosamente tuplas mortas que infam o armazenamento, degradam o desempenho das consultas e, no pior caso, desencadeiam um wraparound catastrófico do ID de transação que coloca todo o banco de dados offline. Este guia cobre tudo que você precisa para configurar o autovacuum corretamente e manter bancos de dados em produção saudáveis.
Pré-requisitos
- PostgreSQL 13 ou superior (a maioria dos exemplos se aplica a partir do PostgreSQL 10+)
psqlou um cliente SQL com acesso em nível DBA- Capacidade de editar
postgresql.confou usarALTER SYSTEM - Familiaridade básica com conceitos de armazenamento do PostgreSQL
Por que o VACUUM existe: MVCC e tuplas mortas
O PostgreSQL usa Controle de Concorrência Multiversão (MVCC) para lidar com leituras e escritas concorrentes sem bloqueios. Cada UPDATE ou DELETE não modifica dados in-place — em vez disso, marca a versão antiga da linha como morta e escreve uma nova versão. Isso faz com que as leituras não bloqueiem, mas deixa para trás tuplas mortas: versões de linhas que não são mais visíveis para nenhuma transação, mas ainda ocupam espaço em disco.
Com o tempo, as tuplas mortas causam três problemas graves:
- Bloat de tabela: O arquivo físico da tabela cresce mesmo que a contagem lógica de linhas permaneça constante. Uma tabela com 10 milhões de linhas ativas pode ocupar o espaço de 50 milhões em disco.
- Bloat de índice: Índices B-tree acumulam ponteiros para tuplas mortas, aumentando seu tamanho e custo de varredura.
- Wraparound de ID de transação: O PostgreSQL usa IDs de transação de 32 bits. Após aproximadamente 2 bilhões de transações, os IDs dão a volta. O PostgreSQL força uma parada de emergência em todo o banco de dados se o VACUUM não conseguir congelar transações antigas a tempo.
O VACUUM resolve os três problemas ao varrer páginas de tabelas, marcando tuplas mortas como espaço reutilizável e avançando o horizonte de congelamento para transações antigas.
Tipos de VACUUM comparados
| Comando | Bloqueio | Recuperação de espaço | Caso de uso |
|---|---|---|---|
VACUUM | Nenhum (compartilha com leituras/escritas) | Marca espaço para reutilização, não encolhe o arquivo | Manutenção rotineira, autovacuum |
VACUUM ANALYZE | Nenhum | Igual + atualiza estatísticas do planejador | Após inserções/deleções em massa |
VACUUM FULL | Exclusivo (tabela bloqueada) | Reescreve a tabela, devolve espaço ao SO | Recuperação pontual de bloat severo |
VACUUM FREEZE | Nenhum | Força congelamento de todos os XIDs antigos | Prevenção de wraparound |
Use VACUUM FULL apenas como etapa de recuperação pontual em tabelas muito infladas — ele mantém um bloqueio exclusivo que bloqueia todas as leituras e escritas durante sua execução. Para reescritas de tabelas online, use pg_repack.
O daemon autovacuum: parâmetros principais
O autovacuum é executado como um daemon em background que lança processos worker para vacuumar tabelas automaticamente. Os parâmetros-chave no postgresql.conf:
# Com que frequência o launcher verifica se há trabalho
autovacuum_naptime = 1min
# Máximo de workers concorrentes de autovacuum
autovacuum_max_workers = 3
# Ativa vacuum quando tuplas mortas > threshold + scale_factor * n_live_tup
autovacuum_vacuum_threshold = 50
autovacuum_vacuum_scale_factor = 0.2 # 20% da tabela
# Ativa ANALYZE quando tuplas modificadas excedem isso
autovacuum_analyze_threshold = 50
autovacuum_analyze_scale_factor = 0.1 # 10% da tabela
# Congela linhas com esta antiguidade de transação
autovacuum_freeze_max_age = 200000000
O ponto crítico de ajuste é autovacuum_vacuum_scale_factor. O padrão de 0.2 significa que o autovacuum dispara quando 20% das linhas de uma tabela estão mortas. Para uma tabela de 100 linhas, isso é aceitável. Para uma tabela de 50 milhões de linhas, o autovacuum não disparará até que 10 milhões de tuplas mortas tenham se acumulado.
Valores globais recomendados para sistemas de alta escrita:
autovacuum_vacuum_scale_factor = 0.05
autovacuum_analyze_scale_factor = 0.02
autovacuum_max_workers = 5
autovacuum_naptime = 30s
Configurações de autovacuum por tabela
A técnica de ajuste mais poderosa é definir parâmetros de autovacuum por tabela, substituindo os padrões globais apenas onde necessário:
-- Para uma tabela de muito alta escrita (ex.: log de eventos/auditoria)
ALTER TABLE eventos SET (
autovacuum_vacuum_scale_factor = 0.01,
autovacuum_vacuum_threshold = 500,
autovacuum_analyze_scale_factor = 0.005,
autovacuum_vacuum_cost_delay = 2
);
-- Para uma tabela de consulta quase estática (vacuum menos agressivo)
ALTER TABLE codigos_pais SET (
autovacuum_vacuum_scale_factor = 0.5,
autovacuum_vacuum_threshold = 1000
);
Monitoramento do bloat de tabelas
Usando pg_stat_user_tables
-- Top 20 tabelas por contagem de tuplas mortas
SELECT
schemaname,
relname AS nome_tabela,
n_live_tup,
n_dead_tup,
round(n_dead_tup::numeric / NULLIF(n_live_tup + n_dead_tup, 0) * 100, 1) AS pct_mortas,
last_vacuum,
last_autovacuum,
last_analyze,
last_autoanalyze
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 20;
Detectar tabelas próximas ao wraparound de XID
SELECT
schemaname,
relname,
age(relfrozenxid) AS idade_xid,
pg_size_pretty(pg_total_relation_size(oid)) AS tamanho_tabela
FROM pg_class
JOIN pg_namespace ON pg_namespace.oid = relnamespace
WHERE relkind = 'r'
ORDER BY age(relfrozenxid) DESC
LIMIT 20;
Qualquer tabela com idade_xid acima de 1,5 bilhão precisa de atenção imediata.
Medir o bloat real com pgstattuple
CREATE EXTENSION IF NOT EXISTS pgstattuple;
SELECT
dead_tuple_count,
round(dead_tuple_percent, 1) AS pct_mortas,
free_space,
round(free_percent, 1) AS pct_livre
FROM pgstattuple('public.pedidos');
Prevenção do wraparound de XID
Monitore a idade no nível do banco de dados:
SELECT
datname,
age(datfrozenxid) AS idade_xid,
pg_size_pretty(pg_database_size(datname)) AS tamanho_bd
FROM pg_database
ORDER BY age(datfrozenxid) DESC;
Recuperação de emergência se um banco estiver próximo do wraparound:
vacuumdb --all --freeze --verbose --analyze
VACUUM FULL vs pg_repack
Para sistemas de produção que não podem tolerar tempo de inatividade, use pg_repack:
sudo apt install postgresql-16-repack
pg_repack -h localhost -U postgres -d meubd -t pedidos
pg_repack -h localhost -U postgres -d meubd --only-indexes -t pedidos
Problemas comuns e resolução
Autovacuum eliminado por transações de longa duração: Busque transações obsoletas:
SELECT pid, now() - xact_start AS duracao, query, state
FROM pg_stat_activity
WHERE xact_start IS NOT NULL
ORDER BY duracao DESC
LIMIT 10;
“Autovacuum: preventing wraparound” nos logs: Significa que o autovacuum entrou em modo de emergência. Não bloqueie nem encerre esses processos de vacuum.
Referência de ajuste por tamanho do banco
| Tamanho do BD | autovacuum_max_workers | autovacuum_vacuum_scale_factor | autovacuum_vacuum_cost_limit |
|---|---|---|---|
| < 10 GB | 3 (padrão) | 0.05 | 200 (padrão) |
| 10–100 GB | 4–5 | 0.02 | 400 |
| 100 GB – 1 TB | 6–8 | 0.01 | 600 |
| > 1 TB | 8–10 + por tabela | 0.005 em tabelas ativas | 800 |
Comparativo entre sistemas de banco de dados
| Recurso | PostgreSQL VACUUM | MySQL OPTIMIZE TABLE | SQL Server Index Rebuild | Oracle Segment Shrink |
|---|---|---|---|---|
| Bloqueios necessários | Nenhum (regular), Exclusivo (FULL) | Exclusivo | Online (Enterprise) | Exclusivo de linha |
| Ferramenta de reescrita online | pg_repack | pt-online-schema-change | Rebuild online | SHRINK SPACE COMPACT |
| Automático | Daemon autovacuum | Manual ou event scheduler | Manutenção automática de índices | Não automático |
| Risco de wraparound | Sim (XID) | Não | Não | Não |
| Visibilidade do bloat | pg_stat_user_tables, pgstattuple | information_schema | sys.dm_db_index_physical_stats | DBA_SEGMENTS |
Resumo
- VACUUM recupera espaço de tuplas mortas de atualizações e deleções MVCC; sem ele, as tabelas incham e o wraparound de XID eventualmente para o banco.
- Autovacuum é a solução automática mas requer ajuste — o padrão
autovacuum_vacuum_scale_factor = 0.2é alto demais para tabelas grandes. - Use configuração por tabela com
ALTER TABLE SETpara tabelas de alta escrita. - Monitore
pg_stat_user_tableseage(datfrozenxid)empg_database. - Use pg_repack em vez de
VACUUM FULLem tabelas de produção para evitar downtime. - Mantenha
age(datfrozenxid)bem abaixo de 1,5 bilhão em todos os bancos.