Home Comunidade Eventos YouTube Networks Desafios // produtos Academy News CTI | CVE Contato

01 - Particionamento Horizontal vs Vertical

#escala#particao#particionamento#postgresql#sharding
video explicativo
Video explicativoEspaco reservado para o video desta anotação — adicione video: https://youtu.be/... no frontmatter da nota.

Particionamento Horizontal vs Vertical

Particionamento é a divisão de uma tabela grande em pedaços menores, gerenciados de forma independente mas consultáveis como uma tabela única; o particionamento horizontal divide por linhas, e o vertical divide por colunas. A distinção decisiva para produção é a fronteira física: quando os pedaços permanecem dentro do mesmo servidor de banco, fala-se em partitioning; quando atravessam servidores distintos, fala-se em sharding, tema detalhado nos arquivos seguintes deste módulo. O particionamento resolve problemas de administração, manutenção e desempenho de consultas em tabelas volumosas, enquanto o sharding resolve problemas de capacidade bruta de CPU, memória e disco de um único nó. Em produção, a decisão entre um e outro determina o modelo operacional do banco por anos, e a escolha equivocada, na direção do sharding prematuro ou do particionamento tardio, está entre as causas mais comuns de projetos de escala atrasados.

Conceito: as quatro combinações possíveis

A matriz conceitual cruza a dimensão de corte com a fronteira física. O corte horizontal fragmenta linhas: pedidos de 2024 em um grupo, de 2025 em outro, ou usuários de ID par e ímpar. O corte vertical fragmenta colunas: as colunas quentes do perfil em uma tabela, os blobs e textos longos em outra. Em cada uma dessas duas dimensões, os pedaços podem ficar no mesmo servidor ou em servidores diferentes, produzindo quatro arranjos com propósitos distintos.

O particionamento horizontal no servidor é o arranjo mais comum no dia a dia do DBA: uma tabela de eventos dividida por mês, com consultas roteadas automaticamente para as partições relevantes. O particionamento vertical é menos visível, mas frequente em sistemas legados que separaram dados frios em tabelas de histórico. O sharding horizontal e o sharding vertical entre servidores são os mesmos cortes, elevados à escala distribuída, com todos os custos de complexidade que a distribuição traz.

O vocabulário entre motores varia e gera confusão em discussões de arquitetura. O MySQL chama as partições horizontais de partitions e não tem particionamento vertical formal; o MongoDB embaralha os termos, pois sua operação de sharding cria chunks internos que funcionam como partições distribuídas; ferramentas como o Citus chamam de shards justamente as partições distribuídas por nós. O referencial adotado neste módulo separa pela fronteira física: mesma máquina, partição; máquinas distintas, shard.

O problema que resolve

Tabelas grandes degradam a operação de forma não linear. Índices deixam de caber na memória, consultas varrem mais blocos, o vacuum do PostgreSQL leva horas em tabelas de bilhões de linhas, e operações de manutenção como REINDEX ou alterações de schema viram janelas de risco. O ponto crítico é que esses problemas aparecem muito antes do esgotamento de capacidade: uma tabela de 500 GB pode ainda caber confortavelmente em disco e já ser um pesadelo de manutenção.

O particionamento horizontal ataca exatamente essa faixa de problema. Cada partição tem seus próprios índices menores, o planejador descarta partições irrelevantes para consultas com filtro na chave, e operações de ciclo de vida se tornam DDL barato: remover o mês antigo é descartar uma partição, operação instantânea, em vez de executar um DELETE que gera milhões de tuplas mortas e horas de vacuum. O particionamento vertical, por sua vez, combate linhas largas: colunas raramente lidas, de tamanho grande, espremem o cache de páginas e penalizam varreduras de colunas realmente quentes.

O diagnóstico correto usa métricas simples. Tempo de vacuum e de manutenção crescente, índices que não cabem em memória e planos que varrem tabelas inteiras são sinais de particionamento; saturação de CPU e de I/O com planos eficientes, fila de conexões e escrita que não acompanha são sinais de limite de nó, que nenhuma partição resolve. Medir antes de escolher evita aplicar a solução errada ao problema certo.

Particionamento horizontal no servidor

O PostgreSQL implementa particionamento declarativo nativo desde a versão 10, com três métodos. RANGE divide por intervalos da chave, tipicamente datas ou identificadores sequenciais; HASH distribui pelo resto da divisão do valor, homogeneizando a carga quando não há ordenação natural; LIST agrupa por valores discretos, como região ou unidade de negócio. A tabela pai define o método e a chave, e cada partição é uma tabela filha real, com índices próprios criados e mantidos em cascata.

-- Tabela de eventos particionada por mês (RANGE)
CREATE TABLE eventos (
    id          bigserial,
    ocorrido_em timestamptz NOT NULL,
    tipo        text NOT NULL,
    payload     jsonb,
    PRIMARY KEY (id, ocorrido_em)      -- a chave da partição deve estar na PK
) PARTITION BY RANGE (ocorrido_em);

-- Partição de janeiro de 2025
CREATE TABLE eventos_2025_01 PARTITION OF eventos
    FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');

-- Distribuição uniforme por hash quando não há eixo temporal
CREATE TABLE usuarios_part (
    id   bigint PRIMARY KEY,
    nome text NOT NULL
) PARTITION BY HASH (id);

CREATE TABLE usuarios_p0 PARTITION OF usuarios_part
    FOR VALUES WITH (MODULUS 4, REMAINDER 0);

O ganho de desempenho vem do partition pruning: consultas com filtro na chave da partição acessam apenas as partições relevantes, reduzindo varredura e uso de cache. O ganho operacional vem do ciclo de vida por DDL: ATTACH e DETACH adicionam e removem partições em produção sem bloqueio prolongado, e DROP descarta dados antigos de forma instantânea, substituindo rotinas de arquivamento inteiras.

Os detalhes de implantação merecem atenção desde o início. Cada partição materializa seus próprios índices, portanto o custo total de memória e planejamento escala com a contagem de partições; restrições de unicidade precisam incluir a chave de partição, o que às vezes exige redesenhar a chave primária; e a criação de partições futuras deve ser automatizada, pois inserção sem partição de destino é erro imediato. Extensões como a pg_partman formalizam a automação de criação e retenção em produção.

Particionamento vertical

O corte vertical separa colunas de uma mesma entidade em tabelas distintas ligadas pela mesma chave primária. O caso de uso clássico divide o quente do frio: as colunas lidas a cada requisição ficam juntas e densas, enquanto textos longos, JSONs históricos e binários migram para uma tabela acessada raramente. Em bancos como o PostgreSQL, o efeito aparece também no mecanismo de TOAST, que já move valores muito grandes para armazenamento separado; o particionamento vertical manual antecipa e controla essa separação.

Os ganhos concretos são três. Páginas de disco ficam mais densas nas colunas quentes, aumentando a proporção de dados úteis em cada leitura; caches de buffer passam a reter mais entidades por página; e permissões e acessos podem ser segregados, como deixar a tabela de documentos sensíveis com política de acesso distinta da tabela de perfil público. O custo é a reconstituição da linha por JOIN quando o consumo precisa do conjunto completo, o que exige atenção em consultas que hoje varrem a tabela inteira sem filtro.

Um exemplo concreto ancora a técnica: em um sistema de RH, a tabela de colaboradores carrega nome, e-mail, matrícula e situação, lidos a cada listagem, junto com histórico salarial, documentos digitalizados e avaliações textuais, lidos raramente. Separar as colunas frias em tabela irmã com a mesma chave primária reduz a tabela quente para uma fração do tamanho original, acelera listagens e relatórios gerais, e ainda simplifica a aplicação de políticas de confidencialidade sobre o conjunto sensível.

Sharding horizontal entre servidores

Quando as partições horizontais atravessam a fronteira do servidor, o arranjo passa a ser sharding, e o problema muda de natureza. O particionamento no servidor mantém transações ACID locais, JOINs nativos e uma única fonte de verdade; o sharding distribui esses mesmos cortes por nós independentes, ganhando capacidade bruta de escrita e armazenamento, e perdendo simplicidade transacional e consistência forte entre fragmentos.

A progressão natural em produção é escalonar verticalmente, particionar no servidor, adicionar réplicas de leitura e, só então, considerar sharding. Os arquivos seguintes deste módulo detalham a chave de sharding, as estratégias de distribuição e os desafios de re-sharding; o ponto a registrar aqui é que o particionamento bem feito é preparação para o sharding, pois tabela já dividida por intervalos ou por hash migra para nós distribuídos com muito menos retrabalho.

Comparação dos arranjos

Aspecto Partição horizontal Partição vertical Shard horizontal Shard vertical
O que se divide Linhas, por intervalo ou hash Colunas da mesma entidade Linhas entre servidores Tabelas entre servidores
Fronteira física Mesmo servidor Mesmo servidor Múltiplos servidores Múltiplos servidores
Problema atacado Manutenção e desempenho de tabelas grandes Linhas largas e colunas frias Capacidade bruta de escrita e dados Escala por domínio de negócio
Transações ACID Locais e completas Locais e completas Limitadas ao fragmento Por banco de domínio
JOIN entre pedaços Nativo, mesmo nó Nativo, mesmo nó Caro, distribuído Passa a ser integração entre serviços
Complexidade operacional Baixa a média Baixa Alta Alta, com governança de dados
Caso típico Eventos, logs, faturamento por mês Perfil com dados frios, documentos Usuários, pedidos em escala massiva Catálogo, pagamentos, identidade separados

A leitura da tabela mostra que os dois particionamentos no servidor são técnicas de engenharia de banco aplicáveis cedo e reversíveis; os dois shardings são decisões arquiteturais caras, com custo permanente de operação, tomadas quando o nó único comprovadamente não alcança.

Particionamento e escala: o que muda e o que não muda

O particionamento melhora três vetores sem mudar a capacidade bruta: manutenção, com ciclo de vida por DDL; desempenho de consulta, com pruning; e contensão de dano, com incidentes confinados a uma partição. Ele não aumenta o poder de CPU, nem a memória, nem o disco do servidor; uma carga de escrita que satura o nó continuará saturando, mesmo com a tabela dividida em duzentas partições.

Essa limitação define o critério de escolha. Particione quando o problema é tamanho e operação de tabela; faça sharding quando o problema é capacidade do nó. Monitorar qual dos dois problemas está ativo é parte do trabalho do time de plataforma: métricas de tempo de vacuum, de tamanho de índice e de manutenção apontam para particionamento; métricas de saturação de CPU, de I/O e de fila de conexões apontam para escala horizontal do próprio banco.

Sinais práticos de que o particionamento basta e o sharding é exagero:

  • Consultas principais filtram naturalmente por um eixo, como data, tenant ou região, permitindo poda eficiente.
  • O volume total cabe no nó atual com folga de crescimento para o horizonte de planejamento do negócio.
  • O problema real é retenção de dados antigos, resolvida por descarte de partição.
  • A escrita satura por falta de indexação ou por transações longas, não por limite físico do hardware.
  • O time de operação é pequeno, sem capacidade de absorver a complexidade permanente de um cluster distribuído.

Aplicação prática: retenção e consultas em produção

O padrão de maior retorno imediato é a retenção de eventos por partição mensal, com rotina de criação antecipada de partições futuras e descarte das antigas. Ferramentas como a extensão pg_partman automatizam esse ciclo, mas a versão manual é pequena e ensina o mecanismo:

-- Consulta com poda de partição: só a partição de janeiro é lida
EXPLAIN SELECT count(*) FROM eventos
 WHERE ocorrido_em >= '2025-01-10' AND ocorrido_em < '2025-01-20';

-- Descarte instantâneo do mês antigo, sem DELETE e sem vacuum
ALTER TABLE eventos DETACH PARTITION eventos_2024_12;

-- Verificação do plano: procure a linha "Append" ou "Seq Scan on eventos_2025_01"
-- confirmando que partições fora do intervalo foram descartadas do plano

Boas práticas complementares incluem: criar partições futuras com antecedência para evitar erro de inserção sem destino; incluir sempre a chave de partição nos filtros e nas chaves primárias; dimensionar partições para caberem inteiras em memória quando possível; e documentar o contrato de retenção, pois partição descartada é dado perdido para sempre. Em cargas analíticas, a combinação de particionamento com zonas de brin ou índices por partição amplia o ganho de varredura, e em cargas transacionais o acompanhamento do planejador nas versões do banco é obrigatório, pois a eficiência do pruning melhora a cada release.

Erros comuns e boas práticas

  • Erro: particionar por coluna ausente na maioria das consultas, anulando o pruning e somando overhead de planejamento; prática: escolher chave que esteja nos predicados dominantes do workload.
  • Erro: usar particionamento como resposta para saturação de escrita; a capacidade do nó não cresce; prática: particionar para manutenção, escalar escrita com réplicas e, no limite, sharding.
  • Erro: criar milhares de partições de dia ou de hora em versões antigas do PostgreSQL, degradando o planejador; prática: dimensionar a granularidade pelo volume real e pela versão em uso.
  • Erro: esquecer a antecedência na criação de partições e receber erro de insert sem partição destino; prática: automação de criação futura com alarme de partição ausente.
  • Erro: particionar verticalmente e negligenciar o JOIN de reconstituição, degradando o caminho de leitura completo; prática: medir os planos dos fluxos principais após o corte.
  • Erro: usar o particionamento como substituto de indexação e ajuste de consultas; partição mal indexada continua lenta; prática: particionar e indexar são trabalhos complementares, não alternativos.
  • Erro: migrar a tabela grande para partições em um único evento sem ensaio; prática: conversão gradual com tabela nova, gatilho ou lógica de gravação dupla e backfill controlado.
  • Erro: presumir que o particionamento elimina a necessidade de monitoramento por tabela; prática: métricas por partição e por índice no observabilidade padrão do banco.

O desenho correto começa por diagnosticar qual problema dói: se o sofrimento é de operação e consulta sobre tabela enorme, particionamento horizontal ou vertical no servidor é a resposta certa e barata; se o sofrimento é de capacidade do nó, particionamento ainda ajuda como preparação, mas a solução está no sharding, com todo o seu custo. Particionar cedo e shardar tarde é a máxima que resume a experiência consolidada de times que escalaram bancos relacionais a sério.