trilha

Tecnologias/05 - Comparações6 min

MongoDB vs Relacional

Conceito

A comparação de 2013 era "SQL vs NoSQL" e virou guerra de religião. Em 2026 os dois lados convergiram tanto que a pergunta mudou de forma:

  • O MongoDB ganhou transação ACID multi-documento, validação de schema, $lookup e SQL-like via Atlas.
  • O PostgreSQL ganhou jsonb com índice GIN — um banco de documentos competente dentro de um relacional.

Então a pergunta útil não é mais "qual é melhor". É:

O meu acesso a dados é sempre por uma raiz conhecida, ou eu preciso cruzar dados de formas que ainda não sei?

Se é sempre por raiz (o pedido, o perfil, o dispositivo, o carrinho), documento é uma representação honesta. Se você precisa cruzar, agrupar e responder perguntas novas, relacional é a escolha — e você vai precisar de JOIN de verdade.

Lado a lado

MongoDB Relacional (Postgres/MySQL/Oracle)
Unidade de dado documento (agregado) linha normalizada
Schema na aplicação (validação opcional) no banco, obrigatório
Modelagem pelas consultas pelas entidades
Junção $lookup (sem otimizador de join) JOIN nativo e otimizado
Integridade referencial não existe FK, cascade, constraint
Transação multi-documento desde 4.0, com custo o caso normal
Atomicidade grátis por documento por linha
Consulta ad-hoc limitada ao previsto excelente
Agregação analítica pipeline (bom, verboso) SQL + window functions
Escala de leitura réplicas réplicas
Escala de escrita sharding nativo vertical → Sharding manual
Evolução de schema escrever no formato novo ALTER TABLE (+ migração)
Consistência configurável por operação forte por padrão
Ferramental de BI exportar / Atlas Federation nativo, universal

Onde cada um ganha, concretamente

MongoDB é a escolha melhor quando:

  • O agregado é a unidade natural de leitura e de escrita — carrinho, sessão, perfil, documento de conteúdo (CMS), configuração de tenant.
  • O schema varia por registro de forma legítima: catálogo de produtos onde cada categoria tem atributos próprios (uma TV tem polegadas, um livro tem ISBN). Em SQL isso é EAV ou 40 colunas nulas — os dois ruins.
  • O volume de escrita precisa passar de um nó, com padrão de acesso conhecido. Sharding é nativo aqui e é projeto no relacional.
  • Séries temporais e ingestão de eventos (com bucket pattern ou time series collections).
  • O produto está em descoberta e o schema muda toda semana.

Relacional é a escolha melhor quando:

  • Há muitos-para-muitos de verdade e o dado é lido por caminhos diferentes.
  • Integridade referencial importa: financeiro, estoque, cadastro regulado.
  • Uma operação toca 4 entidades e precisa ser atômica como regra, não exceção.
  • Relatório e BI são requisito — quem consome é o time de dados, com SQL.
  • As perguntas ao dado vão mudar e você não pode remodelar a cada uma.
O caso em que o MongoDB é a escolha errada e mais escolhida

"Não sei ainda como será o modelo, então uso Mongo porque é flexível." Se você não sabe as consultas, você não pode modelar o documento — e schema flexível é exatamente o oposto do que ajuda aí. Sem consultas definidas, o relacional normalizado é a aposta mais segura: dá para fazer qualquer pergunta depois.

E o jsonb do Postgres?

É a comparação honesta de 2026, e a resposta é matizada:

Postgres jsonb MongoDB
Documento aninhado com índice sim (GIN) sim
Atualização parcial de campo profundo jsonb_set (reescreve o valor) $set em caminho, mais natural
Misturar relacional + documento na mesma query sim, e é o grande argumento não se aplica
Sharding de escrita não nativo nativo
Ergonomia de driver e agregação SQL + funções JSON pipeline nativo
Transação sobre vários documentos trivial possível, com custo

Regra prática: se o documento é uma parte do sistema, jsonb no Postgres. Se o documento é o sistema, e a escrita precisa escalar horizontalmente, MongoDB.

Trade-offs

Usar um como o outro é o pior resultado possível. MongoDB normalizado com $lookup por toda parte tem as desvantagens dos dois: sem JOIN otimizado, sem FK, sem transação por padrão — e ainda paga a complexidade. Postgres usado como key-value com uma coluna jsonb gigante desperdiça tudo o que ele oferece.

O custo de trocar é assimétrico. Sair de relacional para documento é uma remodelagem (dá trabalho, é factível). Sair de documento para relacional exige descobrir o schema que nunca foi declarado a partir de dados reais e inconsistentes. Comece pelo mais restritivo se estiver em dúvida: é mais fácil relaxar depois.

Poliglota é uma resposta legítima, com um custo real. Postgres como fonte da verdade transacional + MongoDB para catálogo/conteúdo é um desenho comum e sensato. O preço é sincronização — que é onde entra Kafka com change streams / CDC, e onde a consistência passa a ser eventual por construção (Eventual Consistency).

O trade-off central: relacional protege você do futuro (qualquer pergunta ainda é possível). Documento otimiza para o presente (a consulta que você tem é rápida e simples). Escolha pelo grau de certeza que você tem sobre as suas consultas — não pela moda nem pela linguagem do driver.

Exemplo prático

Um catálogo de e-commerce, o caso onde a comparação fica nítida:

-- Relacional: atributos variáveis por categoria → EAV, e ninguém gosta de EAV
CREATE TABLE produtos   (id bigint PRIMARY KEY, nome text, preco numeric, categoria_id int);
CREATE TABLE atributos  (produto_id bigint, chave text, valor text);  -- consulta virou pivot

SELECT p.nome
  FROM produtos p
  JOIN atributos a1 ON a1.produto_id = p.id AND a1.chave='polegadas' AND a1.valor='55'
  JOIN atributos a2 ON a2.produto_id = p.id AND a2.chave='resolucao' AND a2.valor='4K';
-- um JOIN por filtro. Com 5 filtros, 5 joins.
// MongoDB: cada produto carrega os próprios atributos
{ _id: 1, nome: "TV X", preco: 2999, categoria: "tv",
  attrs: { polegadas: 55, resolucao: "4K", hdr: true } }

db.produtos.createIndex({ categoria: 1, "attrs.polegadas": 1, "attrs.resolucao": 1 })
db.produtos.find({ categoria: "tv", "attrs.polegadas": 55, "attrs.resolucao": "4K" })
// um filtro, um índice, zero join
-- ✅ E a terceira via, que na prática vence muitos projetos:
CREATE TABLE produtos (
  id bigint PRIMARY KEY,
  nome text NOT NULL, preco numeric(12,2) NOT NULL,   -- relacional onde importa
  categoria_id int REFERENCES categorias(id),          -- FK de verdade
  attrs jsonb NOT NULL DEFAULT '{}'                    -- documento onde varia
);
CREATE INDEX ON produtos USING GIN (attrs);

SELECT nome FROM produtos
 WHERE categoria_id = 7 AND attrs @> '{"polegadas": 55, "resolucao": "4K"}';

A terceira via junta preço com constraint, FK com integridade e atributos flexíveis indexados — na mesma transação e na mesma query. Não é a resposta para todo caso (sharding de escrita não vem incluso), mas é o motivo pelo qual muita equipe que consideraria MongoDB em 2015 fica no Postgres em 2026.

Aprofundamento aplicado

Comparar o custo de mudar uma informação compartilhada

Um catálogo usa o nome do fabricante em milhares de produtos. No desenho normalizado, um registro pode ser a referência corrente. No desenho com cópias, a atualização exige propagar mudanças e decidir o que leitores observam durante essa propagação.

Uma cópia pode ser a escolha correta para leitura frequente ou para preservar história. A pergunta é qual significado ela possui: “nome atual” ou “nome na data da compra”. Os dois modelos conseguem expressar relações e projeções; seus mecanismos e custos diferem.

Mudança de requisito Investigar
Novo relatório cruzando entidades Joins, agregações, índices e projeções
Crescimento de array embutido Limites, paginação e divisão do documento
Regra sobre várias entidades Transação, restrições e conflitos
Mudança de campo duplicado Propagação, versionamento e atraso permitido

Exercício: modele pedido, itens e fabricante das duas formas. Resultado esperado: compare operações de leitura e atualização com regras explícitas, sem concluir que schema flexível elimina contratos ou que normalizar torna toda consulta barata.

Referência: modelagem no MongoDB.

Relacionado


Parte de MOC Comparações · mongodb.com/docs · postgresql.org/docs/current/datatype-json

Buscar

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