Noções Básicas de Particionamento
8 exemplos para você começar com o Particionamento - 5 básicos e 3 intermediários.
Pré-requisitos
- Cluster PostgreSQL 18.4 com privilégios DDL.
- Compreensão dos filtros de consulta (
cláusulas WHERE) e da política de retenção.
Exemplos Básicos
1. Partição por Intervalo (Range) por Mês
Divida eventos de série temporal por mês do calendário - o padrão mais comum.
CREATE TABLE events (
event_id bigint GENERATED ALWAYS AS IDENTITY,
occurred_at timestamptz NOT NULL,
payload jsonb NOT NULL,
PRIMARY KEY (event_id, occurred_at)
) PARTITION BY RANGE (occurred_at);
CREATE TABLE events_2026_01 PARTITION OF events
FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
CREATE TABLE events_2026_02 PARTITION OF events
FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');- A tabela pai não contém linhas - os dados residem nas partições filhas.
- A chave de partição (
occurred_at) deve aparecer nas restriçõesPRIMARY KEYeUNIQUE. - Os limites do intervalo são semiabertos:
FROMinclusivo,TOexclusivo.
Relacionado: DDL de Particionamento Declarativo - configuração pai/filha
2. Partição por Lista (List) por Região
Roteie linhas para partições regionais quando as consultas filtram por um conjunto discreto.
CREATE TABLE orders (
order_id bigint GENERATED ALWAYS AS IDENTITY,
region text NOT NULL,
amount numeric(12, 2) NOT NULL,
PRIMARY KEY (order_id, region)
) PARTITION BY LIST (region);
CREATE TABLE orders_us PARTITION OF orders FOR VALUES IN ('us');
CREATE TABLE orders_eu PARTITION OF orders FOR VALUES IN ('eu', 'uk');
CREATE TABLE orders_apac PARTITION OF orders FOR VALUES IN ('apac');- O particionamento por lista é adequado para chaves de baixa cardinalidade semelhantes a enumerações.
- Agrupe regiões de baixo volume em uma única partição para evitar filhas vazias.
- Consultas com
WHERE region = 'eu'são podadas paraorders_eu.
Relacionado: Poda de Partição - prove que os planos pulam partições
3. Partição por Hash (Hash) para Distribuição Uniforme
Distribua linhas quando não houver um filtro natural de intervalo ou lista.
CREATE TABLE sessions (
session_id uuid NOT NULL,
tenant_id bigint NOT NULL,
created_at timestamptz NOT NULL,
PRIMARY KEY (session_id, tenant_id)
) PARTITION BY HASH (tenant_id);
CREATE TABLE sessions_p0 PARTITION OF sessions FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE sessions_p1 PARTITION OF sessions FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE sessions_p2 PARTITION OF sessions FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE sessions_p3 PARTITION OF sessions FOR VALUES WITH (MODULUS 4, REMAINDER 3);- O hash distribui a carga de escrita por um número fixo de partições.
- O módulo deve corresponder ao número total de partições hash.
- A poda requer
WHERE tenant_id = ?(ou listaIN), não varreduras de intervalo em todos os tenants.
4. Partição Padrão (Default) para Captura
Capture linhas que não correspondem a partições explícitas durante a implantação.
CREATE TABLE events_default PARTITION OF events DEFAULT;
INSERT INTO events (occurred_at, payload)
VALUES ('2027-05-15', '{"type": "signup"}');
-- será inserido em events_default até que você adicione events_2027_05- A partição padrão evita falhas de inserção para chaves não mapeadas.
- Monitore o crescimento da partição padrão - ela desabilita a poda ideal para essas linhas.
- Substitua por partições explícitas e migre as linhas da partição padrão.
Relacionado: Armadilhas de Particionamento - armadilhas da partição padrão
5. Índices em Tabelas Particionadas
Índices na tabela pai são propagados para as filhas existentes e futuras.
CREATE INDEX events_occurred_at_idx ON events (occurred_at);
CREATE INDEX events_payload_gin_idx ON events USING gin (payload);- Crie índices na tabela pai para DDL consistente entre as partições.
- Cada filha armazena seus próprios arquivos de índice físicos.
- Alinhe os índices com predicados amigáveis à poda em
WHERE.
Exemplos Intermediários
6. Anexar Partição Histórica Pré-carregada
Carregue dados offline e, em seguida, anexe como uma partição para uma transição rápida.
CREATE TABLE events_2025_12 (
event_id bigint GENERATED BY DEFAULT AS IDENTITY,
occurred_at timestamptz NOT NULL,
payload jsonb NOT NULL
);
COPY events_2025_12 (occurred_at, payload) FROM '/tmp/events_2025_12.csv' CSV;
ALTER TABLE events ATTACH PARTITION events_2025_12
FOR VALUES FROM ('2025-12-01') TO ('2026-01-01');ATTACHvalida os limites contra a restrição da tabela pai.- Pré-crie restrições
CHECKna tabela independente antes de anexar para validação mais rápida. - Útil para preenchimentos retroativos e importações de arquivamento.
Relacionado: Retenção e Desanexar - desanexar e remover dados antigos
7. Provar a Poda com EXPLAIN
Confirme que o planejador escaneia apenas as partições relevantes.
EXPLAIN (COSTS OFF)
SELECT COUNT(*) FROM events
WHERE occurred_at >= '2026-01-15' AND occurred_at < '2026-02-01';- Procure por
Partitions pruned: Nou nós de escaneamento de filhas apenas paraevents_2026_01. - Sem predicados amigáveis à poda, todas as partições são escaneadas.
- Execute após a atualização das estatísticas:
ANALYZE events;
Relacionado: Poda de Partição - regras de poda
8. Desanexar Partição Antiga para Retenção
Remova um mês da tabela ativa sem excluir linha por linha.
ALTER TABLE events DETACH PARTITION events_2025_01;
-- Opcional: arquive a tabela desanexada e, em seguida, remova-a
ALTER TABLE events_2025_01 RENAME TO events_archive_2025_01;DETACHtransforma a filha em uma tabela independente instantaneamente.- Remova ou arquive a tabela desanexada para recuperar espaço.
- Muito mais rápido do que
DELETE FROM events WHERE occurred_at < ...em tabelas com bilhões de linhas.
Relacionado: Retenção e Desanexar - fluxos de trabalho de arquivamento
Versões da Stack: Esta página foi escrita para PostgreSQL 18.4 (estável 18, manutenção 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x e PostGIS 3.5+.