SQL vs NoSQL: Como Escolher o Banco Certo Antes de Se Arrepender em Produção
← Voltar para Codeshort

SQL vs NoSQL: Como Escolher o Banco Certo Antes de Se Arrepender em Produção

Postgres em tudo parece seguro até a query de feed levar 4 segundos com 300k usuários. Entenda os sinais reais de que você está usando o banco errado.

DC
Dev Code Software
05 de junho de 2026·9 min de leitura

Por que a escolha do banco importa mais do que você pensa

Tem um padrão que aparece em quase todo projeto que cresce rápido: o banco escolhido no início por conveniência vira o gargalo mais difícil de resolver em escala.

Não estou falando de PostgreSQL versus MongoDB como religião. Estou falando de consequência real: um feed de atividade social com 300k usuários que levava 4 segundos para carregar porque foi modelado em tabelas relacionais quando deveria ter sido Redis Sorted Sets. A migração levou três sprints. Os edge cases de consistência duraram meses.

O problema não foi usar Postgres. Foi usar Postgres sem questionar se o modelo relacional servia ao dado.

Essa decisão — que parece técnica e neutra — tem impacto direto na performance, na velocidade de iteração do produto e no custo de infra. E o momento de tomá-la é antes do primeiro CREATE TABLE, não depois do primeiro pico de tráfego.

Quando o SQL é a escolha certa

Antes de discutir os limites do modelo relacional, vale ser explícito sobre onde ele é genuinamente superior — porque a resposta não é "nunca use SQL".

Integridade referencial crítica e transações ACID. Sistemas financeiros, ERP, controle de estoque, pedidos de e-commerce: qualquer cenário onde uma inconsistência de dado é um bug de negócio. O banco relacional reforça as regras pelo próprio mecanismo — chaves estrangeiras, constraints, rollback atômico. Colocar isso na aplicação é possível, mas é débito técnico por design.

Queries ad hoc e relatórios analíticos. SQL tem 50 anos de otimização e uma linguagem de consulta expressiva. GROUP BY, window functions, CTEs recursivas — você consegue responder perguntas que ainda não sabia que faria. Em NoSQL, você modela para as queries que já conhece. Se o produto está em exploração, isso pode travar.

Times sem experiência em bancos distribuídos. Consistência eventual parece simples na teoria. Na prática, produz bugs sutis que só aparecem sob carga concorrente. Para times que ainda estão validando produto, o modelo relacional tem uma complexidade mais previsível.

💡 Dica: Se o time ainda não tem clareza sobre os padrões de acesso do dado, comece com Postgres. Migrar depois de validar é menos custoso do que lidar com inconsistência em produção sem saber de onde vem.

Os sinais de que você está forçando o relacional

Esses antipadrões aparecem em code review, em query plans, nas conversas de arquitetura onde alguém pergunta "por que isso tá lento?". Dois ou mais presentes no mesmo sistema é um sinal claro.

Coluna JSONB como válvula de escape do schema

CREATE TABLE products (
  id UUID PRIMARY KEY,
  name TEXT,
  category TEXT,
  attributes JSONB
);

A justificativa é sempre a mesma: "cada produto tem campos diferentes". Isso não é flexibilidade — é o schema relacional sendo contornado porque o dado não é naturalmente relacional. Você acabou de criar um document store dentro do Postgres, com todo o overhead do Postgres e zero dos benefícios nativos do modelo de documento: sem schema validation por tipo de documento, sem queries idiomáticas, sem índices por campo interno sem configuração extra.

Se chegou nesse ponto, o dado quer ser um documento. A pergunta é se você vai assumir isso conscientemente ou vai continuar adicionando campos JSONB.

Quatro JOINs para montar um único objeto de resposta

SELECT u.*, p.bio, p.avatar_url, s.theme, s.notifications_enabled, a.street, a.city
FROM users u
  LEFT JOIN profiles p ON p.user_id = u.id
  LEFT JOIN settings s ON s.user_id = u.id
  LEFT JOIN addresses a ON a.user_id = u.id
  LEFT JOIN address_types at ON at.id = a.type_id
WHERE u.id = $1;

Isso é normalização usada contra si mesma. O modelo relacional normaliza para evitar redundância e garantir integridade. Mas quando você precisa de quatro JOINs para retornar o objeto que a aplicação vai usar como uma unidade, o dado foi quebrado só para caber no modelo — não porque o modelo serve ao dado.

O próximo passo previsível é criar uma view materializada para pré-computar isso. E depois um cache Redis em cima. No fim, você reinventou o MongoDB na força bruta.

Migration toda sprint para campos opcionais

ALTER TABLE tem custo. Em tabelas grandes, pode travar queries por segundos. Se o schema muda toda sprint porque o produto ainda está descobrindo quais dados precisa guardar, o modelo relacional está gerando atrito — não disciplina.

Nesse estágio, um schema flexível por documento acelera a iteração sem criar risco de consistência, porque ainda não há dados legados para proteger.

⚠️ Atenção: Schema que muda muito não é problema de banco — é sinal de que o domínio ainda não estabilizou. Mas se o dado é naturalmente heterogêneo (produto com atributos diferentes por categoria, eventos com payload variável), o modelo de documento serve estruturalmente melhor.

Onde cada modelo NoSQL resolve um problema diferente

"NoSQL" não é um banco. É uma categoria que abriga modelos completamente distintos, cada um otimizado para um tipo específico de problema. Escolher o modelo errado dentro do NoSQL é tão custoso quanto escolher SQL quando não deveria.

Banco de documentos — MongoDB, Firestore

O dado chega junto e é lido junto. Perfil de usuário completo, configuração de produto por categoria, conteúdo de CMS com campos variáveis por tipo de conteúdo.

const product = await db.collection('products').findOne({ _id: productId });

Uma operação. Um documento. Sem JOINs, sem reconstrução de objeto na aplicação. A troca é que você perde a expressividade do SQL para queries analíticas — o aggregation pipeline do MongoDB é poderoso, mas verboso.

Banco de chave-valor — Redis, DynamoDB

Acesso por chave conhecida, alta frequência, baixa latência. Sessões de usuário, cache de resposta, feature flags, contadores, filas. O Redis não é "banco auxiliar" — para dados com esse padrão de acesso, ele é a escolha primária.

await redis.setex(`session:${userId}`, 3600, JSON.stringify(sessionData));
const session = await redis.get(`session:${userId}`);

O DynamoDB adiciona persistência e escala distribuída, mas exige que os padrões de acesso sejam definidos antes da modelagem. Errar o partition key no DynamoDB tem custo — literalmente, em dólar.

Banco de grafos — Neo4j, Amazon Neptune

Relacionamentos profundos onde o caminho entre entidades importa tanto quanto as entidades em si. Redes sociais, sistemas de recomendação, detecção de fraude por padrão de transação.

MATCH (u:User)-[:FOLLOWS*1..3]->(recommended:User)
WHERE NOT (u)-[:FOLLOWS]->(recommended)
RETURN recommended, count(*) as connections
ORDER BY connections DESC LIMIT 10

Isso em SQL seria uma recursão CTE com múltiplos self-joins — funciona, mas não escala para grafos densos. Em Neo4j, é uma query natural.

Banco de séries temporais — InfluxDB, TimescaleDB

Métricas de infraestrutura, dados de IoT, logs estruturados com timestamp como eixo principal. O volume é alto, a escrita é contínua, e as queries são sempre agregações temporais: média dos últimos 5 minutos, pico por hora, comparação semana a semana.

O modelo relacional clássico não foi projetado para esse padrão — índices em timestamp com alta cardinalidade ficam grandes rápido e as queries de rollup ficam lentas em escala.

💡 Dica: TimescaleDB é extensão do Postgres. Se você já opera Postgres e precisa de séries temporais, comece por aí — você ganha otimizações de hypertable sem adicionar uma nova engine ao stack.

Comparativo prático: SQL vs MongoDB vs Redis vs Neo4j

CritérioSQL (Postgres)Documento (MongoDB)Chave-Valor (Redis)Grafo (Neo4j)
Integridade referencial✅ Nativa no banco❌ Responsabilidade da aplicação❌ Sem suporte⚠️ Parcial
Schema flexível❌ Migrations obrigatórias✅ Por documento✅ Sem schema⚠️ Parcial
Queries ad hoc complexas✅ SQL completo⚠️ Aggregation pipeline❌ Muito limitado✅ Cypher
Leitura pontual por ID⚠️ Depende de índice✅ Alta✅ Sub-milissegundo⚠️ Depende da profundidade
Relacionamentos profundos❌ JOINs custosos em escala❌ Ruim por design❌ Inexistente✅ Nativo e eficiente
Consistência eventualNão (ACID)ConfigurávelSim (por padrão)Depende da config
Escalabilidade horizontal⚠️ Sharding complexo✅ Nativa✅ Nativa (cluster)⚠️ Limitada em escala
Curva de operaçãoConhecidaModeradaBaixaAlta

O erro mais caro: "vou migrar depois"

A lógica é sedutora: começo com Postgres porque conheço bem, e quando o produto crescer e os problemas aparecerem, eu migro.

O problema é que migração de banco em produção é uma das operações mais complexas e arriscadas que existem. Não porque seja tecnicamente impossível — é porque a aplicação acumulou anos de suposições sobre o banco que ninguém documentou. Você vai ter dados em dois lugares ao mesmo tempo. Vai precisar de dual-write com reconciliação. Vai ter janelas de inconsistência que dependendo do dado são inaceitáveis. E vai descobrir que algumas queries eram possíveis só pela estrutura relacional e precisam ser repensadas do zero.

O caso real: um sistema de feed social com Postgres ficou com query de 4 segundos aos 300k usuários. A migração para Redis Sorted Sets — tecnicamente correta — levou três sprints inteiros. E os edge cases de consistência entre o dado novo e o legado duraram meses em produção.

Isso não significa que você nunca pode mudar de banco. Significa que o custo é real e não linear — quanto mais tempo o sistema opera com o banco errado, mais difícil é sair.

Como decidir na prática: o checklist de padrão de acesso

A escolha do banco não deve partir de familiaridade — deve partir do padrão de acesso do dado. Cinco perguntas que devem guiar a decisão antes do primeiro commit:

1. Como o dado chega? Em lote via import, em eventos individuais em tempo real, em streaming contínuo? Cada padrão favorece modelos diferentes.

2. Como o dado é lido? Um objeto por vez (→ documento ou chave-valor), listas paginadas com filtros (→ relacional ou documento), agregações analíticas (→ relacional ou série temporal), travessias por relacionamento (→ grafo).

3. O relacionamento entre entidades é central ao negócio? Se sim, e se esses relacionamentos têm múltiplos saltos (amigos de amigos, produtos comprados por quem comprou X), avalie grafo. Se os relacionamentos são simples foreign keys, relacional resolve bem.

4. Qual é a proporção escrita/leitura e a frequência? Alta frequência de escrita com leitura eventual → série temporal ou chave-valor. Leitura muito mais frequente que escrita → foque em estrutura de leitura otimizada (documento, Redis).

5. O schema vai estabilizar em quanto tempo? Produto em descoberta ativa com mudanças semanais → documento ou relacional com migrations controladas. Domínio estável com contratos claros → relacional com schema versionado.

Se mais de três respostas apontarem para o mesmo modelo, a decisão está clara. Se apontarem para modelos diferentes, você provavelmente tem um sistema que se beneficia de persistência poliglota — cada contexto com o banco mais adequado.

FAQ

Posso usar SQL e NoSQL no mesmo projeto? Sim, e em sistemas maduros isso é o padrão — cada contexto usa o banco mais adequado ao seu dado. O risco é operacional: mais engines para monitorar, mais infra para manter, mais onboarding necessário no time. A recomendação é começar com um banco e adicionar outro quando o problema for concreto e justificado, não preventivo.

MongoDB é lento? Ouço isso com frequência. MongoDB sem índices adequados é lento. Com queries que fazem collection scan em coleções grandes, é muito lento — equivalente a full table scan no SQL. A performance do MongoDB está diretamente ligada à qualidade da modelagem e dos índices. "MongoDB é lento" geralmente significa "MongoDB foi usado sem pensar no padrão de acesso".

Como fica a consistência com NoSQL? Depende do banco e da configuração. MongoDB tem transações ACID multi-documento desde a versão 4.0. Redis com AOF persistence tem durabilidade configurável. "NoSQL não tem consistência" é um mito de 2012. O que é verdadeiro é que a consistência eventual é o padrão default em sistemas distribuídos — você precisa configurar explicitamente o nível de consistência que o seu dado exige.

DynamoDB: quando faz sentido de verdade? Quando você conhece os padrões de acesso antes de modelar — e não vai mudá-los com frequência. DynamoDB tem escalabilidade praticamente sem limite e latência consistente, mas o custo de queries ineficientes é literal: RCUs consumidos por full scans são caros. É a escolha certa para sistemas com acesso previsível e alto volume. Para produtos em descoberta, é a escolha errada.

Preciso aprender todos esses bancos ao mesmo tempo? Não. Domine Postgres e aprenda um banco de documentos (MongoDB ou Firestore). Adicione Redis para cache e sessão — isso cobre 90% dos cenários. O resto você aprende quando o problema aparecer com dados reais, não de forma preventiva.

Resumo acionável

Se você está no início de um projeto, a decisão deve ser guiada pelos padrões de acesso — não pela familiaridade ou pelo que a empresa usa como padrão.

Se você tem um projeto em produção com atrito no banco atual:

  1. Mapeie as queries mais lentas e mais frequentes. Isso revela o padrão de acesso real, não o que você imaginou no início.
  2. Identifique os antipadrões presentes. Colunas JSONB, JOINs excessivos para montar objetos simples, migrations semanais — cada um desses é um sinal de modelo errado.
  3. Isole um contexto antes de migrar tudo. Comece pelo sistema de notificações, pelo feed de atividade, pelo módulo de sessão — valide o novo modelo com dado real antes de comprometer o sistema inteiro.
  4. Defina a métrica de sucesso antes de começar. Tempo de query, throughput, custo de infra — sem número, você não sabe se a migração valeu.

A escolha do banco é uma decisão de arquitetura com consequências de longo prazo. Não precisa ser perfeita desde o início. Mas precisa ser feita com base em evidência, não em hábito.