⌘K

06 – Por que o seu Postgres precisa de VACUUM? Entendendo o Bloat e o MVCC

Last updated

Se você vem de outros bancos de dados relacionais, ou se está acostumado a apenas criar tabelas e rodar queries, existe um comportamento no PostgreSQL que pode te pegar de surpresa: o banco continua consumindo cada vez mais espaço em disco, mesmo depois de você deletar milhões de registros.

Você roda um DELETE massivo, limpa tabelas históricas, mas o uso de armazenamento do servidor não cai um megabyte sequer. Por que isso acontece? O Postgres está quebrado?

Não, isso é uma característica de arquitetura. No post de hoje da nossa série Guia de Sobrevivência: Backup e Manutenção no Postgres, vamos entender o conceito de MVCC, o que é o temido Bloat (inchaço) e como o utilitário de linha de comando vacuumdb entra em ação para salvar o seu disco.

O Coração do Postgres: Entendendo o MVCC

Para entender o VACUUM, precisamos primeiro entender o MVCC (Multi-Version Concurrency Control ou Controle de Concorrência de Múltiplas Versões).

O MVCC é o mecanismo que permite que o Postgres seja incrivelmente rápido e seguro ao lidar com acessos simultâneos. Graças a ele, uma query de leitura (SELECT) que está rodando em uma tabela não trava e nem é travada por uma operação de escrita (UPDATE ou DELETE) na mesma linha.

Para que isso funcione sem que um usuário veja o dado incompleto do outro, o Postgres trabalha com versões de linhas (tuples).

O que acontece em um DELETE?

Quando você deleta uma linha, o Postgres não apaga o registro do disco imediatamente. Ele apenas marca aquela linha fisicamente como “invisível” (uma dead tuple ou linha morta) para as transações futuras.

O que acontece em um UPDATE?

Um UPDATE no Postgres é, na verdade, um DELETE seguido de um INSERT. A linha antiga é marcada como morta (invisível) e uma linha totalmente nova com os dados atualizados é gravada no final do arquivo.

O Problema: O que é o Bloat (Inchaço)?

Com o passar do tempo, sua aplicação faz milhares de UPDATES e DELETES. O arquivo físico da sua tabela no disco vai ficando cheio dessas linhas mortas espalhadas entre as linhas vivas. Esse fenômeno é chamado de Bloat (Inchaço).

Imagine um livro onde, em vez de apagar uma linha errada com borracha, você apenas risca a caneta e escreve a frase certa na linha de baixo. O livro vai acumulando páginas cheias de riscos.

E aqui está o detalhe crucial: o espaço em disco não volta para o sistema operacional automaticamente. O Postgres mantém aquele espaço reservado dentro do arquivo da tabela para reutilizá-lo em futuros INSERTS.

Por que o Bloat é ruim?

  1. Desperdício de Armazenamento: Você pode ter 10GB de dados reais escondidos dentro de um arquivo de 50GB no disco.
  2. Perda de Performance: Quando você rodar um SELECT que precisa escanear a tabela (Sequential Scan), o Postgres terá que ler do disco todas as linhas vivas e todas as linhas mortas, jogando a performance do seu banco ladeira abaixo.

O Salvador: O comando VACUUM

É aqui que entra o processo de faxina. O VACUUM passa varrendo a tabela, encontra essas linhas mortas e diz ao Postgres: “Ok, essas linhas aqui ninguém mais está usando. Pode marcar esse espaço como livre para que novos INSERTs usem esse pedaço do arquivo”.

O VACUUM padrão (ou Lazy Vacuum):

  • Não trava a tabela: Sua aplicação continua rodando, lendo e escrevendo normalmente enquanto a limpeza acontece.
  • Não diminui o tamanho do arquivo no disco: Ele apenas reorganiza o espaço interno do arquivo.

O Gancho: Entrando em produção com o vacuumdb

O Postgres possui um mecanismo nativo chamado Autovacuum, que roda em background fazendo essa limpeza. Porém, em ambientes de produção com alta carga de escrita, o Autovacuum pode não dar conta ou rodar em momentos inapropriados, gerando gargalos de I/O.

Como um profissional de infraestrutura (DevOps/DBA), você pode tomar as rédeas da situação usando o vacuumdb, o utilitário de linha de comando que permite agendar e gerenciar limpezas profundas fora do horário de pico.

Exemplos Práticos do vacuumdb

1. Faxina Completa em um Banco Específico

Para rodar o vacuum em todas as tabelas do banco meu_app mostrando o progresso na tela:

Bash

vacuumdb -U postgres -h localhost -d meu_app -v

2. Limpeza Paralela em Larga Escala (Acelerando o Processo)

Se o seu banco é grande e o servidor tem recursos de hardware sobrando na madrugada, use a flag -j para rodar a faxina em paralelo usando múltiplos cores de CPU (limpando várias tabelas simultaneamente):

Bash

vacuumdb -U postgres -h localhost -d meu_app -j 4 -v

3. Foco Cirúrgico: Limpando apenas uma tabela crítica

Se você acabou de rodar uma rotina pesada que atualizou milhões de linhas em uma tabela específica (ex: historico_pedidos), você não precisa rodar no banco todo. Vá direto ao ponto:

Bash

vacuumdb -U postgres -h localhost -d meu_app -t public.historico_pedidos -v

Conclusão

Entender o MVCC e o Bloat é fundamental para manter um banco de dados Postgres saudável e veloz a longo prazo. O VACUUM não é um comando opcional; ele é o oxigênio que impede o seu banco de sufocar em dados obsoletos.

Mas atenção: se o inchaço da tabela ficou crítico e você precisa desesperadamente devolver espaço físico para o sistema operacional porque o HD do servidor está batendo 99% de uso, o VACUUM padrão não vai resolver. Você vai precisar de uma medida drástica chamada VACUUM FULL.

No próximo post da nossa série, vamos analisar o confronto: VACUUM FULL vs VACUUM padrão, os perigos de travar a sua aplicação e alternativas para mitigar o temido downtime. Até lá!

E aí, você já percebeu o banco de dados crescendo misteriosamente mesmo deletando dados? Já conhecia o vacuumdb para agendar essa automação? Comente aqui embaixo!

Still stuck? How can we help? Get Help