Skip to content

NoSQL

Bancos NoSQL ("não apenas SQL") são uma família de bancos mais flexíveis que o modelo relacional, pensada para grandes volumes, formatos variados e escala horizontal. Não substituem o relacional: cada modelo resolve melhor um tipo de problema. Conceitos de SQL e modelagem relacional ficam em SQL e Modelagem de Dados.

SQL x NoSQL

SQL (relacional) NoSQL
Estrutura Tabelas com esquema definido e relações entre registros Documentos, chave-valor, grafos ou colunas
Esquema Definido antes de salvar Pode variar conforme a necessidade
Consistência Prioriza transações e regras estruturadas Muitos priorizam escala e flexibilidade
Quando usar Finanças, pedidos, cadastros, sistemas com muitos relacionamentos Grandes volumes, formatos variados, alta escala de leitura ou escrita

Os principais modelos

Banco de documentos

Armazena registros como documentos, em geral parecidos com JSON; cada documento pode conter campos, listas e objetos aninhados, e dois documentos da mesma coleção podem ter campos diferentes. Exemplo: um produto guardando nome, preço, imagens, categorias e avaliações em um único documento. Combina bem com APIs e aplicações que trabalham com JSON. Cuidado: evite documentos gigantes e pense em como os dados serão consultados e atualizados. (Exemplo clássico: MongoDB.)

Chave-valor

Cada dado é guardado como uma chave única ligada a um valor (usuario:42 → preferências ou dados de sessão). A busca por chave é simples e muito rápida, com baixa latência e fácil escalabilidade. Usos típicos: cache, sessões, carrinho de compras, contadores e dados temporários. Limite: não é a melhor escolha para consultas complexas com muitos filtros e relacionamentos. (Exemplo: Redis.)

Banco de colunas (famílias de colunas)

Agrupa dados em famílias de colunas, não em tabelas rígidas. Lida bem com grande volume de escrita distribuída em vários servidores; exige modelar as colunas pensando nas consultas que a aplicação realmente fará. Exemplo: eventos por usuário e data, logs de acesso, telemetria. Cuidado: relacionamentos e consultas ad hoc são mais limitados que em bancos relacionais. (Exemplo: Cassandra.)

Banco de grafos

Representa nós (entidades) e arestas (relações entre elas): pessoa → segue → pessoa; cliente → comprou → produto. Encontra caminhos, conexões, recomendações e graus de relacionamento sem encadear muitos JOINs. Aplicações: redes sociais, detecção de fraude, rotas, recomendações e gestão de dependências. Cuidado: escolha quando as conexões forem centrais; não substitui todo tipo de banco. (Exemplo: Neo4j.)

Séries temporais

Registros acompanhados por data e hora: medições, eventos, leituras de sensores (preço de ações, uso de CPU, vendas por hora). A ordem do tempo é essencial; agregados por minuto, hora, dia ou mês aceleram análises. Usos: monitoramento, IoT, métricas de aplicações, finanças, previsões. Cuidado: defina retenção, compressão e índices de tempo para grandes volumes.

Busca textual

Localiza documentos pelo conteúdo de textos longos, usando um índice de texto que organiza termos para busca mais rápida do que ler cada texto inteiro. Oferece palavras-chave, frases, filtros, sinônimos e correção de termos, com resultados ordenados por relevância. Exemplo: buscar produtos por descrição ou artigos por assunto. Cuidado: atualize os índices e trate idioma, acentos e palavras muito comuns (stop words, ver Machine Learning).

Dados em sistemas distribuídos

Teorema CAP

Em sistemas distribuídos, servidores podem ficar separados por falhas de rede. O teorema CAP diz que não dá para garantir ao mesmo tempo as três propriedades abaixo durante uma partição:

Definição: Teorema CAP

  • C — Consistência (Consistency): toda leitura recebe a versão mais recente do dado ou uma mensagem de erro.
  • A — Disponibilidade (Availability): toda requisição recebe uma resposta, mesmo durante falhas.
  • P — Tolerância a partição (Partition tolerance): o sistema continua funcionando quando há perda de comunicação entre nós.

Como partições de rede acontecem, na prática a escolha é entre priorizar consistência ou disponibilidade quando ela ocorre. Entenda a necessidade do produto antes de definir arquitetura e banco.

Consistência eventual

Definição: Consistência eventual

Após uma atualização, algumas réplicas podem demorar um pouco para mostrar o novo dado; sem novas mudanças, elas convergem para o mesmo valor. Ajuda a manter disponibilidade e escala em sistemas distribuídos.

  • Quando usar: métricas, feeds, catálogos e dados que toleram uma pequena demora (ex.: uma curtida ou contador pode aparecer diferente por alguns segundos em regiões distintas).
  • Cuidado: não use em operações críticas que exigem leitura imediata e exata, como saldo bancário.

Comparar com as garantias ACID das transações relacionais em SQL — transações.

MongoDB em profundidade

O MongoDB é o banco de documentos mais usado. Esta seção mostra como ele funciona na prática, sempre comparando com o SQL que você já conhece. (Os exemplos usam o mongosh, o shell atual.)

Conceitos

Definição: documento, collection e database no MongoDB

Documento é um registro em formato JSON que reúne todas as informações de uma entidade — pode ter valores simples, listas e outros documentos aninhados. Collection é um conjunto de documentos (equivale a uma tabela, mas sem esquema fixo). Database agrupa collections. Cada documento tem um campo _id único, indexado automaticamente.

Definição: JSON e BSON

JSON é um formato textual simples (objetos {}, listas [], textos, números, booleanos, null) criado para trafegar dados entre servidor e navegador. Ele não define data nem dado binário, por isso o MongoDB usa o BSON (Binary JSON), uma extensão binária com mais tipos (Date, ObjectId, inteiros de 32/64 bits, decimal, binário).

Dois conceitos de escala: replica set (cópias do mesmo banco em vários servidores, para alta disponibilidade) e sharding (particionar uma collection entre servidores quando ela passa do que cabe em um só).

CRUD e operadores, comparado ao SQL

Operação SQL MongoDB (mongosh)
Criar INSERT INTO pedidos ... db.pedidos.insertOne({...}), insertMany([...])
Consultar SELECT * FROM pedidos WHERE total > 100 db.pedidos.find({ total: { $gt: 100 } })
Projeção SELECT cliente, total find({}, { cliente: 1, total: 1, _id: 0 })
Ordenar e limitar ORDER BY total DESC LIMIT 5 find().sort({ total: -1 }).limit(5)
Atualizar UPDATE ... SET status = 'pago' updateOne({ _id: 1 }, { $set: { status: "pago" } })
Incrementar SET saldo = saldo + 10 updateOne(filtro, { $inc: { saldo: 10 } })
Remover DELETE FROM pedidos WHERE ... deleteOne(filtro), deleteMany(filtro)
Valores distintos SELECT DISTINCT status db.pedidos.distinct("status")
Contar SELECT COUNT(*) countDocuments(filtro)
Remover tabela DROP TABLE db.pedidos.drop()

Operadores começam com $:

Tipo Operadores Equivalente em SQL
Comparação $gt, $gte, $lt, $lte, $ne >, >=, <, <=, <>
Conjunto $in, $nin IN, NOT IN
Lógicos $and, $or, $nor, $not AND, OR, NOT
Existência $exists: true/false IS NOT NULL (útil porque o esquema é flexível)
Texto { nome: /silva$/i } ou $regex LIKE '%silva' (o i ignora maiúsculas; ^ e $ ancoram início e fim)

Validação e coleções especiais. Embora o esquema seja flexível, é possível exigir estrutura com um validator ($jsonSchema: tipos, campos obrigatórios, padrões). Uma capped collection tem tamanho fixo e descarta os documentos mais antigos (um buffer circular, bom para logs recentes).

Modelagem (schema design): a aplicação manda

No modelo relacional você normaliza pensando nos dados; no MongoDB você modela pensando em como a aplicação lê. Se uma tela exibe um prêmio com seus autores, tipo e ano, o ideal é um documento com tudo (em vez de juntar cinco tabelas). É a mesma ideia da desnormalização do data warehouse, aplicada ao banco operacional. "De quantas tabelas preciso para exibir isto? Cinco. De quantas collections? Uma."

Relacionamento Opção 1: embutir (embedded) Opção 2: referenciar
Um para muitos (livro → comentários) Array de comentários dentro do livro: uma leitura, melhor desempenho Duas collections e uma busca por livro_id: lembra o relacional, sem vantagem no MongoDB, exceto se os filhos forem enormes ou muito acessados à parte
Muitos para muitos (livros ↔ categorias) Cada lado guarda a lista de _id do outro (sem tabela intermediária)
Tudo numa collection Mais intuitivo e rápido Documentos grandes (limite de 16 MB), dados repetidos para atualizar, índices em subdocumentos mais complexos

Exemplo: um filme com categorias e diretores como listas simples e atores como documentos aninhados.

db.filmes.insertOne({
  titulo: "Exemplo", ano: 1999, nota: 8.1, votos: 52000,
  categorias: ["Drama", "Crime"],
  diretores: ["Pessoa A"],
  atores: [ { nome: "Pessoa B", sexo: "F" }, { nome: "Pessoa C", sexo: "M" } ]
})

Critério prático: embuta o que é lido junto e tem ciclo de vida junto (e não cresce sem limite); referencie o que é compartilhado, muito grande, ou acessado de forma independente.

Migrando de um banco relacional: uma rotina lê as tabelas, monta o documento (juntando listas de tabelas auxiliares) e o insere na collection. Dicas para cargas grandes: insertMany em lotes em vez de um documento por vez, e relaxar o write concern (por padrão o cliente espera a confirmação de cada gravação; w: 0/unacknowledged não espera, e é mais rápido, mas sem garantia). Para a nuvem existe o MongoDB Atlas, com camada gratuita.

Aggregation framework

Consultas de agregação (o GROUP BY do SQL) usam um pipeline: uma lista de estágios em que a saída de um alimenta o próximo.

db.pedidos.aggregate([
  { $match: { status: "pago" } },                         // WHERE
  { $group: { _id: "$cliente", total: { $sum: "$valor" },  // GROUP BY cliente
              pedidos: { $sum: 1 }, media: { $avg: "$valor" } } },
  { $match: { total: { $gt: 1000 } } },                   // HAVING
  { $sort: { total: -1 } },                               // ORDER BY
  { $limit: 10 }
])

Outros estágios: $project (escolher/calcular campos), $unwind (desmembrar uma lista em um documento por item), $lookup (equivalente a um JOIN entre collections), $addFields, $facet. O antigo Map Reduce (a ideia de dividir o trabalho em map e reduce para processar em paralelo, como fazer vários cachorros-quentes ao mesmo tempo) foi substituído pelo aggregation framework e está obsoleto. O MongoDB Compass monta pipelines de forma visual.

Busca geoespacial

Com coordenadas guardadas num campo (de preferência no formato GeoJSON), cria-se um índice "2dsphere" (ou "2d" para plano) e consultam-se os documentos perto de um ponto ($near, com $maxDistance) ou dentro de uma área ($geoWithin) — o caso de "municípios vizinhos" ou "lojas mais próximas".

Índices e desempenho

Sem índice, uma consulta lê a collection inteira (COLLSCAN). O comando explain("executionStats") mostra o plano de execução: nReturned (retornados), totalDocsExamined (lidos), totalKeysExamined (chaves de índice lidas) e executionTimeMillis. Uma consulta saudável tem IXSCAN e totalDocsExamined próximo de nReturned. No exemplo do livro, criar um índice no campo de ano derrubou a consulta de cerca de 1,8 s para 0,13 s (e de 1,3 milhão de documentos lidos para 13 mil).

Índice Uso
Simples createIndex({ ano: 1 }) Filtros e ordenação por um campo (1 = crescente, -1 = decrescente; a direção só importa em compostos)
Composto createIndex({ ano: 1, nota: -1 }) Consultas por vários campos; a ordem dos campos importa
De array (multikey) Indexa cada elemento de uma lista (categorias)
Em campo aninhado Indexe o caminho completo (atores.sexo); indexar só o campo pai não ajuda a consultar o filho
Único (unique: true) Impede duplicatas (falha se já existirem; um nome de município repete em vários estados, então o único é por nome + estado)
TTL (expireAfterSeconds) Apaga documentos automaticamente após um tempo (logs, sessões) usando um campo de data
Parcial Indexa só os documentos que atendem a um filtro (menor e mais barato)
Texto Busca textual por palavras (full text search)

O índice de _id não pode ser removido. Índices custam espaço e tornam a escrita mais lenta: crie só os que as consultas usam. Desde a versão 4.2 a criação de índices não bloqueia mais o banco, o que tornou a opção de criar "em background" desnecessária. Mesmo com bons índices, nada supera um bom schema design. Veja o princípio geral em SQL.

Administração

  • Armazenamento: o motor padrão WiredTiger (desde a versão 3) gerencia compressão e memória sozinho; o MongoDB usa a memória disponível como cache (o ideal é o working set caber na RAM). compact reorganiza o espaço de uma collection.
  • Scripts: o shell tem um interpretador JavaScript (variáveis, laços, funções), útil para rotinas de DBA, como listar o stats de cada collection. As funções JavaScript armazenadas no servidor (stored functions) são limitadas e desencorajadas; prefira o pipeline de agregação.
  • Segurança: por padrão a autenticação vem desligada (facilidade de uso). Em produção, habilite autenticação (usuários e senhas), autorização por papéis (role-based, por banco e collection), TLS e restrição de rede. Veja Administração de banco.

Replica set e sharding

flowchart LR
    APP[Aplicação] --> R[mongos<br/>query router]
    R --> S1[(Shard 1<br/>replica set)]
    R --> S2[(Shard 2<br/>replica set)]
    R --> S3[(Shard 3<br/>replica set)]
    R -.metadados.-> C[(Config servers<br/>replica set)]
  • Replica set: um nó primário recebe as escritas; os secundários replicam. Os nós trocam heartbeats a cada 2 segundos e, se o primário cai, os demais elegem um novo (por isso, de preferência, um número ímpar de nós/árbitro). Nós em máquinas diferentes dão alta disponibilidade de fato (rodar vários nós no mesmo servidor só serve para teste). Comandos: rs.initiate(), rs.add(), rs.status().
  • Sharding: quando uma collection chega a bilhões de documentos e não cabe em um servidor, ela é dividida por uma chave de partição (shard key) entre vários shards (cada um um replica set). Três componentes: shards (dados), config servers (metadados) e mongos (roteador de consultas). A escolha da shard key é decisiva (boa distribuição e cardinalidade, evitar chaves monotônicas como data crescente, que concentram as escritas num shard). Detalhes de particionamento em Administração de banco.

Transações

O MongoDB nasceu com atomicidade por documento: uma gravação em um documento (mesmo com subdocumentos) é atômica, e o modelo embutido procura aproveitar isso, evitando transações. Desde a versão 4.0 há transações ACID multidocumento (em replica sets e, depois, em clusters particionados):

const sessao = db.getMongo().startSession()
sessao.startTransaction()
const contas = sessao.getDatabase("banco").contas
contas.updateOne({ _id: "A" }, { $inc: { saldo: -100 } })
contas.updateOne({ _id: "B" }, { $inc: { saldo: 100 } })
sessao.commitTransaction()   // ou abortTransaction()

Dentro da transação, outras sessões só veem os dados depois do commit. Se duas transações alteram o mesmo documento, a segunda falha com WriteConflict e a aplicação deve tratar e tentar de novo. Transações têm custo; no MongoDB o ideal é ainda modelar para precisar delas o mínimo.

Nota

A fonte, de uma versão antiga, afirma que o MongoDB "não suporta transação" e "não substitui um banco relacional". Hoje a primeira afirmação não vale mais (veja acima); a segunda continua sendo uma questão de adequação: a escolha entre eles está em Como escolher o banco.

Limites úteis

Documento: 16 MB, até 100 níveis de aninhamento; collection: até 64 índices; nomes de campo, collection e database têm limites de tamanho. O nome "Mongo" vem de humongous (enorme).

Como escolher o banco

flowchart TD
    A["Dados: estrutura, volume,<br/>relacionamentos, crescimento"] --> E["Escolha"]
    B["Consultas: leituras e escritas<br/>mais importantes"] --> E
    C["Consistência: exata agora<br/>ou pode atualizar depois?"] --> E
    D["Escala e equipe: picos, regiões,<br/>quem opera e mantém"] --> E
  1. Dados: analise estrutura, volume, relacionamentos e velocidade de crescimento.
  2. Consultas: liste as leituras e escritas mais importantes antes de modelar.
  3. Consistência: defina se o dado precisa ser exato imediatamente ou pode atualizar depois.
  4. Escala: considere usuários, picos, regiões, disponibilidade e necessidade de distribuição.
  5. Equipe: escolha a tecnologia que a equipe consiga operar, monitorar e manter.

Regra de ouro

Comece simples, meça a necessidade real e evite tecnologia só por moda. Em muitos sistemas, um banco relacional bem modelado resolve; vários produtos reais combinam mais de um tipo (ex.: relacional para pedidos + chave-valor para cache + busca textual para o catálogo).