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?
- Desperdício de Armazenamento: Você pode ter 10GB de dados reais escondidos dentro de um arquivo de 50GB no disco.
- Perda de Performance: Quando você rodar um
SELECTque 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!