O Modelo de Particionamento
O particionamento divide uma tabela lógica em várias peças físicas, permitindo que o código da aplicação continue consultando-a como se fosse uma única tabela.
Essa divisão parece um detalhe de armazenamento, mas a chave de partição que você escolhe se torna uma promessa estrutural sobre como cada consulta futura, índice e trabalho de manutenção se comportará.
Esta página constrói o modelo mental por trás do particionamento declarativo do PostgreSQL, para que as páginas mais mecânicas desta seção (sintaxe DDL, poda, retenção) façam sentido como consequências desse modelo, em vez de receitas isoladas.
Resumo
- Uma tabela particionada é uma tabela lógica cujas linhas são distribuídas fisicamente entre várias tabelas filhas, escolhidas por uma chave de partição.
- Por que Importa: O particionamento transforma operações caras em tabelas inteiras (exclusão em massa, vacuum, manutenção de índices) em operações baratas por partição, mas apenas no eixo que a chave de partição define.
- Conceitos Chave: chave de partição, poda de partições, particionamento por intervalo/lista/hash, partição padrão, operação particionada por tabela.
- Quando Usar: Tabelas grandes onde dados antigos são regularmente arquivados ou excluídos, e onde a maioria das consultas filtra em uma única coluna estável.
- Limitações / Compensações: Consultas que não filtram na chave de partição não obtêm benefício de poda e podem ser mais lentas do que uma tabela não particionada equivalente.
- Tópicos Relacionados: poda de partições, estratégia de retenção, design de índices em tabelas particionadas, inchaço de tabelas (table bloat).
Fundamentos
Uma tabela particionada no PostgreSQL é declarada uma vez, com PARTITION BY RANGE, LIST ou HASH em uma coluna de chave de partição escolhida.
Cada partição filha é uma tabela física real que contém uma fatia definida de linhas, e a própria tabela pai não armazena nenhuma linha.
CREATE TABLE events (
event_id bigint GENERATED ALWAYS AS IDENTITY,
occurred_at timestamptz NOT NULL,
PRIMARY KEY (event_id, occurred_at)
) PARTITION BY RANGE (occurred_at);Particionamento por intervalo (Range partitioning) atribui linhas a uma partição com base em onde um valor se encaixa entre limites, e é o ajuste natural para dados de séries temporais como eventos ou logs.
Particionamento por lista (List partitioning) atribui linhas por associação a valores exatos, o que se encaixa em colunas de baixa cardinalidade como região ou nível de tenant.
Particionamento por hash (Hash partitioning) distribui linhas uniformemente por um número fixo de partições usando um hash da chave, para casos onde não existe um agrupamento natural por intervalo ou lista, mas a distribuição uniforme ainda é importante.
A escolha entre esses três é realmente uma escolha sobre em que as futuras consultas filtrarão, porque é isso que determina se o PostgreSQL pode pular partições inteiramente.
Mecânicas e Interações
A capacidade do planejador de pular partições irrelevantes é chamada de poda de partições (partition pruning), e é todo o argumento de desempenho para o particionamento.
Quando a cláusula WHERE de uma consulta restringe a chave de partição a um intervalo ou a um conjunto de valores, o planejador pode determinar no momento do planejamento (ou em alguns casos no momento da execução) quais partições filhas podem possivelmente conter linhas correspondentes, e escanear apenas essas.
EXPLAIN (COSTS OFF)
SELECT * FROM events WHERE occurred_at >= '2026-01-01' AND occurred_at < '2026-02-01';
-- Um plano podado toca apenas a partição events_2026_01Se uma consulta não filtra na chave de partição, a poda não pode ocorrer, e a consulta escaneia todas as partições, o que pode ser mais lento do que a consulta equivalente em uma tabela não particionada devido ao sobrecarga adicional de planejamento.
É por isso que a chave de partição não é um detalhe de implementação de armazenamento para escolher por último; é uma previsão sobre a forma dominante da cláusula WHERE no fluxo de trabalho de consulta real da tabela.
Índices em uma tabela particionada são declarados uma vez na tabela pai e o PostgreSQL propaga a definição para todas as partições filhas existentes e futuras, mas cada filha ainda armazena e mantém seu próprio índice físico.
Isso significa que adicionar um índice a uma tabela particionada com muitas partições é, na verdade, adicionar muitos índices de uma vez, e deve ser planejado com o mesmo cuidado que qualquer outro DDL de tabela grande.
Uma partição padrão (default partition) existe para capturar linhas que não correspondem a nenhum limite de partição explícito, o que evita falhas de inserção durante a implantação, mas tem um custo: linhas na partição padrão não podem ser podadas de nenhuma consulta, pois o planejador não pode descartá-la para nenhum predicado.
Considerações Avançadas e Aplicações
O caso de manutenção do particionamento é frequentemente o argumento mais forte na prática, mesmo antes da velocidade da consulta.
Excluir um mês de dados históricos de uma tabela de um bilhão de linhas não particionada significa um DELETE lento e com muito log de transações, que precisa ser vacuumed depois; desanexar uma partição é uma operação rápida de metadados que remove os dados instantaneamente.
ALTER TABLE events DETACH PARTITION events_2025_01;Vacuum e autovacuum também se beneficiam do particionamento indiretamente, pois cada partição é uma tabela separada para fins de autovacuum, então uma partição recente e ativa, que é atualizada frequentemente, pode ser vacuumed sem tocar em partições históricas frias que não mudaram.
A escolha entre particionamento por intervalo, lista e hash em escala real geralmente se resume a qual deles corresponde à forma real de filtro em produção, não qual deles é teoricamente mais elegante para os dados.
| Abordagem | Força | Fraqueza | Melhor Ajuste |
|---|---|---|---|
| Particionamento por intervalo | Ajuste natural para dados de séries temporais; suporta retenção fácil via detach | Requer criação contínua de partições à medida que o tempo avança | Tabelas de eventos, logs e séries temporais |
| Particionamento por lista | Mapeamento direto para um conjunto conhecido e pequeno de valores | Novos valores precisam de novas partições ou um padrão "catch-all" | Região, nível de tenant ou outras colunas de baixa cardinalidade |
| Particionamento por hash | Distribuição de escrita uniforme sem chave de intervalo ou lista natural | Nenhum benefício de poda, a menos que as consultas filtrem na chave de hash com igualdade | Tabelas de alta escrita que precisam de distribuição, não acesso baseado em tempo ou categoria |
| Sem particionamento | Mais simples de operar; nenhuma necessidade de planejamento de poda | Custo de exclusão em massa e vacuum escala com o tamanho total da tabela | Tabelas que permanecem pequenas ou cujas exclusões são raras |
O particionamento também interage com as regras de chave primária e constraints únicas no PostgreSQL: qualquer constraint única, incluindo a chave primária, deve incluir a chave de partição como parte de suas colunas, pois a unicidade só pode ser aplicada dentro de cada partição física, não em todas elas de uma vez.
Essa constraint molda o design da chave mais cedo do que a maioria das equipes espera, e vale a pena resolvê-la durante o estágio de modelagem lógica em vez de descobri-la ao escrever a primeira instrução CREATE TABLE PARTITION BY.
Concepções Errôneas Comuns
- "O particionamento sempre torna as consultas mais rápidas." O particionamento só ajuda as consultas cujos predicados correspondem à chave de partição de perto o suficiente para que a poda se aplique; uma consulta que filtra em uma coluna não relacionada pode ficar mais lenta, não mais rápida.
- "A chave de partição é apenas uma escolha de armazenamento." A chave de partição é uma aposta estrutural no padrão de consulta dominante, e escolhê-la sem observar as formas reais das cláusulas
WHEREtende a produzir uma tabela que não consegue podar as consultas que realmente rodam contra ela. - "Uma partição padrão é um 'catch-all' permanente e seguro." Uma partição padrão evita falhas de inserção, mas as linhas dentro dela nunca podem ser podadas, e deixá-la crescer mina todo o argumento de desempenho para particionar essa tabela.
- "O particionamento substitui a necessidade de índices." Índices e particionamento resolvem problemas diferentes; uma tabela particionada ainda precisa dos mesmos índices que uma tabela não particionada teria, aplicados por partição.
- "Posso particionar em qualquer coluna mais tarde sem custo." A chave de partição se torna parte de cada constraint única, incluindo a chave primária, então mudá-la após o fato geralmente significa reconstruir a tabela, não um
ALTERleve.
Perguntas Frequentes
O que realmente é uma tabela particionada no PostgreSQL?
É uma tabela lógica declarada com PARTITION BY, apoiada por múltiplas tabelas filhas físicas que cada uma contém uma fatia definida de linhas.
A própria tabela pai não armazena nenhuma linha.
Como escolho entre particionamento por intervalo, lista e hash?
- Intervalo: dados de séries temporais ou ordenados, mais comumente uma coluna de timestamp.
- Lista: colunas de baixa cardinalidade e valores conhecidos, como região ou nível.
- Hash: distribuição de escrita uniforme quando não há agrupamento natural por intervalo ou lista.
O que é poda de partições e por que ela importa?
A poda de partições é a capacidade do planejador de pular tabelas filhas que não podem possivelmente conter linhas correspondentes, com base na cláusula WHERE da consulta.
É o principal benefício de desempenho do particionamento, e só é ativado quando uma consulta filtra na chave de partição.
O que acontece se uma consulta não filtrar na chave de partição?
O planejador não pode descartar nenhuma partição, então ele escaneia todas elas, o que pode ser mais lento do que consultar uma tabela não particionada equivalente devido ao sobrecarga adicional de planejamento por partição.
Por que a chave de partição deve fazer parte da chave primária?
Constraints de unicidade, incluindo a chave primária, só podem ser aplicadas dentro de uma única partição física, não em todas as partições de uma vez.
Incluir a chave de partição na constraint é como o PostgreSQL garante que a unicidade global seja realmente mantida.
Excluir dados antigos é mais rápido com particionamento?
Sim, quando dados antigos se alinham com partições inteiras - desanexar ou descartar uma partição é uma operação rápida de metadados, em comparação com um DELETE linha por linha que depois precisa ser vacuumed em uma tabela não particionada.
Índices precisam ser criados em cada partição filha separadamente?
Não - um índice declarado na tabela pai se propaga para todas as partições filhas existentes e futuras automaticamente.
Cada filha ainda armazena sua própria cópia física desse índice.
O que é uma partição padrão e quando devo usá-la?
Uma partição padrão captura linhas que não correspondem aos limites de nenhuma partição explícita, evitando falhas de inserção durante a implantação ou valores inesperados.
Ela deve ser monitorada e mantida pequena, pois as linhas dentro dela nunca podem ser podadas.
O particionamento ajuda com vacuum e autovacuum?
Indiretamente, sim - cada partição é uma tabela separada para fins de autovacuum, então uma partição recente e ativa pode ser vacuumed frequentemente enquanto partições históricas frias são deixadas intocadas.
Posso alterar a chave de partição de uma tabela depois que ela já está em produção?
Não como uma operação leve - a chave de partição afeta todas as constraints únicas na tabela, então mudá-la geralmente requer a reconstrução da tabela sob um novo esquema de particionamento.
É por isso que a chave merece uma análise real antes que a primeira partição seja criada.
O particionamento é principalmente um recurso de desempenho ou um recurso de manutenção?
Ambos, mas o caso de manutenção (retenção rápida via detach, vacuum isolado por partição) é frequentemente a vitória mais forte e confiável em produção, antes da velocidade bruta da consulta.
A velocidade da consulta depende inteiramente se a poda realmente se aplica.
Quantas partições são muitas?
Não há um número universal, mas o sobrecarga de planejamento cresce com a contagem de partições, e uma contagem de partições ilimitada e em constante crescimento (uma por dia para sempre, sem retenção) eventualmente sobrecarrega o tempo de planejamento, mesmo quando a poda funciona corretamente.
Políticas de retenção existem em parte para manter isso limitado.
Relacionados
- Noções Básicas de Particionamento - exemplos práticos de intervalo, lista e hash
- DDL de Particionamento Declarativo - sintaxe de tabela pai e filha em profundidade
- Poda de Partições - provando e depurando planos de consulta podados
- Retenção e Detach - arquivando e descartando partições antigas
- Armadilhas de Particionamento - armadilhas de partição padrão e outros erros
- Fundamentos de Modelagem de Dados - o processo de modelagem ao qual a decisão da chave de partição pertence
Versões da Stack: Esta página foi escrita para PostgreSQL 18.4 (estável 18, manutenção 17).