trilha

System Design/03 - Qualidade/Performance Antipatterns2 min

Monolithic Persistence

Perguntas-guia
  • Que problema isso resolve?
  • Quando usar / quando NÃO usar?
  • Qual o principal trade-off?
  • Como isso falha em produção?

Conceito

Usar um único banco para todos os tipos de dado do sistema, ignorando que perfis de acesso incompatíveis competem entre si pelo mesmo recurso.

Perfis que não deveriam compartilhar o mesmo banco

Tipo de dado Perfil Banco adequado
Transacional Escrita pequena, ACID Relacional
Analítico Varredura de milhões de linhas Colunar (Redshift, ClickHouse)
Log / telemetria Escrita massiva, leitura por tempo Série temporal
Sessão / cache Chave, TTL, altíssima frequência Key-Value Store
Busca textual Índice invertido, relevância Elasticsearch, OpenSearch
Blob Arquivos grandes Armazenamento de objetos

O caso mais destrutivo é analítico junto de transacional: um relatório que varre 50 milhões de linhas satura o buffer pool, expulsa as páginas quentes do transacional e degrada todo o sistema — durante os minutos em que roda.

A resposta é persistência poliglota: cada tipo de dado no armazenamento adequado, que é a mesma ideia de Federation aplicada por perfil em vez de por domínio.

Trade-offs

Poliglota custa caro: mais tecnologias para operar, mais expertise necessária, mais pontos de falha, e o fim das junções e transações entre os armazenamentos. Consistência entre eles passa a ser eventual, mantida por replicação ou eventos.

Por isso o antipadrão oposto é igualmente real — adotar cinco bancos por princípio, num sistema que um Postgres bem ajustado atenderia inteiro. Bancos relacionais modernos cobrem muito mais terreno do que a moda sugere: jsonb cobre documento, extensões cobrem série temporal e busca textual, e a operação continua sendo de um sistema só.

O critério prático é a dor medida. Separe quando um perfil estiver comprovadamente prejudicando outro — não antes. E, quando separar, comece pelo maior ofensor: normalmente é o analítico ou a telemetria, e mover apenas esse já resolve a maior parte do problema.

Vale lembrar que a primeira separação costuma ser a mais barata de todas e nem exige outro banco: mandar relatórios para uma réplica de leitura (Replication).

Exemplo prático

Um único Postgres guardando pedidos, sessões e 200 GB de eventos de telemetria.

O que acontece: a tabela de eventos domina o buffer pool; consultas de pedido passam a ler do disco; o VACUUM da tabela de eventos consome I/O em horário de pico; e o backup, que precisa cobrir tudo, leva horas.

A separação por ordem de retorno:

  1. Eventos para um banco de série temporal — resolve a maior parte, e a tabela era 90% do volume
  2. Sessões para Redis — elimina escrita de altíssima frequência e o job de expurgo
  3. Relatórios para uma réplica — tira a varredura do primário

Pedidos ficam onde estavam, num banco que voltou a caber em memória. Nenhuma linha de lógica de negócio mudou.

Relacionado


Parte de Performance Antipatterns · roadmap.sh/system-design

Buscar

Busca por título, seção e texto das notas