Particionamento de tabelas no PostgreSQL: quando e como usar
1. Introdução ao particionamento de tabelas
Particionamento de tabelas é uma técnica de design de banco de dados que divide uma tabela grande em partes menores e mais gerenciáveis, chamadas partições. No PostgreSQL, o particionamento nativo foi introduzido na versão 10 e refinado significativamente nas versões 11 e 12. Antes disso, a comunidade utilizava herança de tabelas como solução alternativa, mas o particionamento nativo oferece melhor integração com o otimizador de consultas e funcionalidades como partition pruning.
A principal diferença entre particionamento lógico e herança de tabelas é que, no particionamento nativo, o PostgreSQL gerencia automaticamente a rota dos dados para as partições corretas, enquanto na herança o desenvolvedor precisa criar triggers e regras manuais. Os benefícios incluem: performance superior em queries que filtram pela chave de partição, manutenção simplificada (como descartar partições inteiras em vez de deletar milhões de linhas), e gerenciamento mais eficiente de dados volumosos.
2. Tipos de particionamento suportados no PostgreSQL
O PostgreSQL suporta quatro tipos principais de particionamento:
Particionamento por intervalo (RANGE): Ideal para dados ordenados como datas, timestamps ou IDs sequenciais. Cada partição contém um intervalo contínuo de valores.
CREATE TABLE vendas (
id SERIAL,
data_venda DATE NOT NULL,
cliente_id INTEGER,
valor NUMERIC
) PARTITION BY RANGE (data_venda);
CREATE TABLE vendas_2024_q1 PARTITION OF vendas
FOR VALUES FROM ('2024-01-01') TO ('2024-04-01');
CREATE TABLE vendas_2024_q2 PARTITION OF vendas
FOR VALUES FROM ('2024-04-01') TO ('2024-07-01');
Particionamento por lista (LIST): Adequado para categorias discretas como status, regiões ou tipos de produto.
CREATE TABLE pedidos (
id SERIAL,
status TEXT NOT NULL,
cliente_id INTEGER,
total NUMERIC
) PARTITION BY LIST (status);
CREATE TABLE pedidos_pendentes PARTITION OF pedidos
FOR VALUES IN ('pendente', 'aguardando');
CREATE TABLE pedidos_finalizados PARTITION OF pedidos
FOR VALUES IN ('entregue', 'cancelado');
Particionamento por hash (HASH): Distribui dados uniformemente entre partições sem significado semântico, útil para balanceamento de carga.
CREATE TABLE usuarios (
id SERIAL,
nome TEXT,
email TEXT
) PARTITION BY HASH (id);
CREATE TABLE usuarios_part1 PARTITION OF usuarios
FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE usuarios_part2 PARTITION OF usuarios
FOR VALUES WITH (MODULUS 4, REMAINDER 1);
Particionamento composto (subparticionamento): Combina estratégias, como particionar por ano e depois por mês.
CREATE TABLE logs (
id SERIAL,
timestamp TIMESTAMPTZ NOT NULL,
nivel TEXT,
mensagem TEXT
) PARTITION BY RANGE (timestamp);
CREATE TABLE logs_2024 PARTITION OF logs
FOR VALUES FROM ('2024-01-01') TO ('2025-01-01')
PARTITION BY LIST (nivel);
CREATE TABLE logs_2024_erro PARTITION OF logs_2024
FOR VALUES IN ('ERROR', 'CRITICAL');
3. Quando usar particionamento: cenários e indicadores
O particionamento é recomendado quando:
- Tabelas com milhões ou bilhões de registros e crescimento contínuo — a manutenção se torna inviável sem particionamento.
- Queries que filtram frequentemente por uma chave de partição — o PostgreSQL elimina partições irrelevantes automaticamente.
- Necessidade de remover dados antigos rapidamente — um
DROP TABLEem uma partição é instantâneo comparado a umDELETEque gera vacuum e bloqueia linhas. - Cargas de trabalho ETL que processam janelas de dados — como dados diários ou mensais.
Quando NÃO usar particionamento:
- Tabelas com menos de 10 milhões de registros — o overhead de planejamento pode superar os benefícios.
- Chaves de partição inadequadas que criam partições desbalanceadas.
- Dados que raramente são filtrados pela chave de partição.
- Sistemas com muitas operações de atualização que mudam a chave de partição (rows movendo entre partições).
4. Como implementar particionamento no PostgreSQL
A sintaxe básica começa com a tabela mestre:
CREATE TABLE transacoes (
id BIGSERIAL,
data_transacao DATE NOT NULL,
conta_id INTEGER,
valor NUMERIC(15,2)
) PARTITION BY RANGE (data_transacao);
Depois, criamos as partições filhas:
CREATE TABLE transacoes_2024_01 PARTITION OF transacoes
FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');
CREATE TABLE transacoes_2024_02 PARTITION OF transacoes
FOR VALUES FROM ('2024-02-01') TO ('2024-03-01');
Para migrar tabelas existentes, usamos ATTACH PARTITION:
-- Primeiro, criar tabela com estrutura idêntica
CREATE TABLE transacoes_antigas (LIKE transacoes INCLUDING ALL);
-- Preencher com dados existentes
INSERT INTO transacoes_antigas SELECT * FROM transacoes_old;
-- Anexar como partição
ALTER TABLE transacoes ATTACH PARTITION transacoes_antigas
FOR VALUES FROM ('2023-01-01') TO ('2024-01-01');
Ferramentas como pg_partman automatizam esse processo, criando e gerenciando partições automaticamente.
5. Gerenciamento de partições na prática
Adicionando novas partições automaticamente: Podemos usar funções e pg_cron para criar partições futuras:
CREATE OR REPLACE FUNCTION criar_particao_mensal()
RETURNS void AS $$
DECLARE
mes_inicio DATE;
mes_fim DATE;
BEGIN
mes_inicio := date_trunc('month', now() + interval '1 month');
mes_fim := mes_inicio + interval '1 month';
EXECUTE format(
'CREATE TABLE transacoes_%s PARTITION OF transacoes
FOR VALUES FROM (%L) TO (%L)',
to_char(mes_inicio, 'YYYY_MM'),
mes_inicio,
mes_fim
);
END;
$$ LANGUAGE plpgsql;
Removendo partições antigas: Extremamente rápido e eficiente:
DROP TABLE transacoes_2023_01;
Dividindo partições: Para ajustar limites:
ALTER TABLE transacoes SPLIT PARTITION transacoes_2024_01
AT ('2024-01-15')
INTO (transacoes_2024_01_a, transacoes_2024_01_b);
6. Performance e planos de execução com particionamento
O PostgreSQL utiliza partition pruning para eliminar partições que não contêm dados relevantes. Ao executar uma query com filtro na chave de partição, o planejador analisa os limites e ignora partições desnecessárias:
EXPLAIN ANALYZE SELECT * FROM vendas
WHERE data_venda BETWEEN '2024-01-15' AND '2024-01-20';
O plano mostrará apenas a partição relevante sendo escaneada, resultando em leitura de menos blocos e menor uso de CPU.
Índices locais vs. globais: O PostgreSQL não suporta índices globais (índices únicos em todas as partições). Cada partição pode ter seus próprios índices:
CREATE INDEX idx_vendas_data ON vendas_2024_q1 (data_venda);
CREATE INDEX idx_vendas_cliente ON vendas_2024_q2 (cliente_id);
Cuidados com joins: Joins entre tabelas particionadas funcionam bem se ambas usarem a mesma chave de partição (partition-wise join), recurso disponível desde o PostgreSQL 12.
7. Boas práticas, limitações e armadilhas comuns
Escolha correta da chave de partição: Evite partições desbalanceadas. Por exemplo, particionar por status onde 99% dos registros têm status "ativo" anula os benefícios.
Limitações importantes:
- Não é possível criar chaves estrangeiras entre partições diferentes.
- Não existe constraint UNIQUE global — cada partição tem sua própria unicidade.
- Updates que movem uma linha entre partições são possíveis, mas caros.
Impacto no vacuum: Cada partição é tratada individualmente pelo autovacuum, o que é bom para paralelismo mas requer configuração cuidadosa.
Monitoramento: Use views do sistema para acompanhar:
SELECT
schemaname,
tablename,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as tamanho
FROM pg_tables
WHERE tablename LIKE 'vendas_%'
ORDER BY tablename;
8. Conclusão e referências
O particionamento de tabelas no PostgreSQL é uma ferramenta poderosa quando aplicada corretamente. Os pontos críticos são: escolher a chave de partição adequada, monitorar o balanceamento entre partições, e automatizar a criação/remoção de partições. Ferramentas como pg_partman e pg_pathman simplificam significativamente o gerenciamento.
Lembre-se: particionamento não é solução para todos os problemas de performance. Teste sempre com seus dados reais e cargas de trabalho típicas antes de implementar em produção.
Referências
- Documentação Oficial do PostgreSQL: Particionamento de Tabelas — Guia completo com sintaxe, exemplos e limitações do particionamento nativo.
- PostgreSQL Partitioning: A Complete Guide (2024) — Tutorial prático com cenários reais e comparação de performance.
- pg_partman: Partition Management Extension — Extensão oficial para automação de gerenciamento de partições.
- Using Partitioning in PostgreSQL for Better Performance — Artigo da EnterpriseDB com casos de uso e melhores práticas.
- PostgreSQL Partitioning: When and How to Use It — Blog da Percona com análise de performance e armadilhas comuns.
- PostgreSQL Partition Pruning Explained — Documentação específica sobre como o PostgreSQL otimiza consultas em tabelas particionadas.