Skip to main content

Como ajustar o autovacuum do PostgreSQL para ambientes em alta escala

By 17 de junho de 2026Institucional

Para quem sustenta ambientes PostgreSQL em produção, poucas palavras geram tantos debates quanto “bloat” e “vacuum”. Em sistemas de pequeno ou médio porte, a configuração padrão do banco costuma dar conta do recado de forma invisível. No entanto, conforme o volume de dados cresce e a taxa de transações por segundo (TPS) dispara, os valores default deixam de ser eficientes, transformando-se em um gargalo silencioso de I/O ou em uma bomba-relógio de degradação de performance.

Como parte da nossa visão de engenharia no conceito Postgres 360°, este post não visa ensinar comandos básicos. O foco aqui é puramente tático e estratégico: entender os trade-offs e aprender a calibrar os manípulos corretos do autovacuum para garantir previsibilidade e resiliência na sua infraestrutura.

O Custo da Concorrência: Por que o Vacuum é Vital?

O Postgres utiliza o mecanismo de MVCC (Multiversion Concurrency Control) para gerenciar transações simultâneas. Quando um registro é atualizado ou deletado, o motor do banco não apaga o dado fisicamente do disco de imediato. Em vez disso, ele marca aquela linha como semanticamente excluída (criando uma dead tuple ou tupla morta).

O acúmulo dessas tuplas mortas é o que chamamos de bloat. Se o processo de vacuum não passar de tempos em tempos para varrer e liberar esse espaço para novas gravações, as tabelas e índices se expandem artificialmente. O resultado? Consultas lentas (pois precisam ler mais páginas de disco para trazer o mesmo resultado) e desperdício massivo de armazenamento.

Além do controle de bloat, o vacuum possui uma missão de sobrevivência do cluster: prevenir o Transaction ID (XID) Wraparound. O contador de transações do Postgres usa 32 bits (cerca de 4 bilhões de transações). Metade disso é usado no passado e metade no futuro. Se o banco atingir o limite de 2 bilhões de transações sem que o vacuum execute o congelamento (freeze) das tuplas antigas, o PostgreSQL entrará em modo de segurança e desligará para evitar downtime não planejado em horários de pico.

A Anatomia do Gatilho Padrão (E por que ele falha em escala)

Por padrão, o processo do autovacuum decide se uma tabela precisa de intervenção baseando-se na seguinte fórmula matemática:

$$\text{Gatilho de Vacuum} = \text{autovacuum\_vacuum\_threshold} + (\text{autovacuum\_vacuum\_scale\_factor} \times \text{reltuples})$$

Nas configurações nativas do PostgreSQL, os valores são:

  • autovacuum_vacuum_threshold = 50 linhas
  • autovacuum_vacuum_scale_factor = 0.2 (ou seja, 20% da tabela)

Para uma tabela de 1.000 linhas, o cálculo faz total sentido: o vacuum é ativado após cerca de 250 modificações.

O cenário muda quando aplicamos isso à escala real. Imagine uma tabela crítica de histórico ou logs com 100 milhões de registros. Pelas regras padrão, o banco esperará acumular 20 milhões de tuplas mortas antes de disparar o processo. Processar esse volume gigantesco de uma só vez gera um pico severo de contenção de I/O, degrada a performance das consultas de produção e eleva drasticamente os riscos arquiteturais.

Ajustando os Manípulos Táticos do Autovacuum

Para evitar cenários reativos e manter a previsibilidade do seu ambiente, os DSEs e líderes técnicos devem ajustar os parâmetros diretamente no nível da tabela ou globalmente no postgresql.conf.

1. Fatores de Escala (Scale Factors)

Em tabelas volumosas, a melhor prática é reduzir o scale factor drasticamente ou ignorá-lo, fixando um limite absoluto de tuplas mortas. Em ambientes de alta escala, o ideal é que o vacuum ocorra próximo à marca de cada 2 milhões de linhas alteradas.

SQL

-- Exemplo: Ajustando o fator de escala para 1% em uma tabela de grande volume
ALTER TABLE transacoes_criticas SET (autovacuum_vacuum_scale_factor = 0.01);

2. Controle de Custo de I/O e Escalonamento (Throttling)

Para evitar que o autovacuum consuma toda a largura de banda do seu subsistema de disco, o PostgreSQL adota um sistema de pontuação de custo. Conforme o vacuum lê páginas compartilhadas no buffer, páginas fora dele ou grava modificações, ele acumula “pontos”. Ao atingir o limite definido, ele pausa por um tempo determinado.

  • autovacuum_vacuum_cost_limit: O teto de custo acumulado antes da pausa (padrão de 200).
  • autovacuum_vacuum_cost_delay: O tempo de descanso após atingir o limite (padrão de 2ms).

Se o seu processo está demorando muito e acumulando bloat, você pode aumentar o limite de custo ou reduzir o delay. Se ele está gerando picos de latência na aplicação, aumente o delay para suavizar o impacto.

SQL

-- Reduzindo o impacto de I/O forçando pausas maiores (Aumentando o delay para 5ms)
ALTER TABLE transacoes_criticas SET (autovacuum_vacuum_cost_delay = '5ms');

3. Mitigando o risco de Wraparound (Freeze Max Age)

O parâmetro autovacuum_freeze_max_age (geralmente fixado em 200 milhões de transações) age como uma válvula de segurança agressiva. Quando qualquer tabela atinge essa idade computada em pg_class.relfrozenxid, o Postgres força uma varredura completa (anti-wraparound vacuum), ignorando os mapas de visibilidade.

Em tabelas massivas e altamente transacionais, essa leitura forçada pode acontecer em horários críticos de pico. Elevar esse limite para 400 ou 500 milhões de transações — desde que haja planejamento e monitoramento contínuo — oferece uma janela maior para o banco respirar e permite agendar manutenções preventivas em horários de menor tráfego.

SQL

-- Ampliando a margem antes de um vacuum agressivo forçado por wraparound
ALTER TABLE transacoes_criticas SET (autovacuum_freeze_max_age = 400000000);

Diagnóstico e Previsibilidade: Como Auditar seu Ambiente?

Tomar decisões de tunagem sem dados reais é um risco operacional alto. Para obter visibilidade tática sobre quais tabelas estão acumulando resíduos e próximas de seus limites de disparo, os times de engenharia podem monitorar o catálogo estatístico do PostgreSQL.

Sua infraestrutura tem gargalos invisíveis? Use essa query de auditoria hoje e veja se seu ambiente está performando no limite ideal:

SQL

SELECT
    relname AS nome_tabela,
    n_dead_tup AS tuplas_mortas,
    last_autovacuum,
    last_vacuum
FROM
    pg_stat_user_tables
ORDER BY
    n_dead_tup DESC;

Se ao rodar suas auditorias você constatar que o nível de bloat de uma tabela de grande porte está constantemente acima de 50%, ou que o indicador xmin mais antigo nos logs permanece travado (frequentemente causado por transações longas abertas ou conexões órfãs que impedem a limpeza), é um forte indicativo de que a estratégia precisa ser reavaliada.

O Caminho para a Maturidade

Ajustar o autovacuum não é uma tarefa de “configuração única”. Trata-se de uma análise contínua de trade-offs: balancear a frequência de escrita no disco com a performance das leituras da aplicação.

Ao evoluir a maturidade da sua infraestrutura, saindo dos padrões genéricos e adotando parametrizações cirúrgicas para as características de cada tabela, sua organização ganha em estabilidade, previsibilidade física de armazenamento e máxima performance do ecossistema PostgreSQL.

 

Faça parte da nossa newsletter

Share