Fundamentos & Arquitetura

Fundamentos & Arquitetura
MongoDB

Do documento BSON à arquitetura interna: como o MongoDB guarda dados, replica para alta disponibilidade e distribui carga por sharding. Cada tópico traz teoria, exemplo prático em mongosh, um caso de uso do dia a dia e link para a documentação oficial.

10 tópicos
mongosh + drivers oficiais
Foco: conceitos + arquitetura interna
Recursos gerais
01Crie um cluster gratuito (M0) no MongoDB Atlas para praticar sem instalar nada localmente
02Use o mongosh (shell oficial) ou o MongoDB Compass (GUI) para explorar dados visualmente
03Sempre que uma query estiver lenta, rode .explain("executionStats") antes de otimizar às cegas
04Consulte o MongoDB Manual para a sintaxe exata de cada operador antes de usar em produção
01

Fundamentos

O modelo de dados orientado a documentos

Documentos e BSONBásico
A unidade básica de dados no MongoDB

Um documento é a unidade básica de dados no MongoDB — um registro estruturado em pares chave-valor, semelhante a um objeto JSON. Mas o que é gravado em disco não é texto JSON: é BSON (Binary JSON), um formato binário que adiciona tipos que o JSON puro não tem, como Date, ObjectId, Decimal128 e binário puro.

TeoriaBSON foi desenhado para ser rápido de percorrer e rápido de codificar/decodificar: cada elemento carrega seu próprio comprimento em bytes, o que permite ao MongoDB pular campos sem precisar interpretar o documento inteiro — algo que um parser de JSON texto não consegue fazer. Isso tem um custo: BSON é menos compacto que JSON puro (os comprimentos ocupam espaço extra), mas o ganho em velocidade de leitura/escrita compensa amplamente para um banco de dados. O tamanho máximo de um documento BSON é 16 MB — um limite proposital para evitar que um único documento monopolize memória e banda de rede durante a transferência.
Dia a diaUm perfil de usuário de e-commerce — nome, e-mail, endereços de entrega (lista) e histórico dos últimos pedidos — cabe inteiro em um único documento, sem precisar de JOIN entre "tabelas" de endereço e pedido para montar a tela de perfil.
mongosh
// Documento de exemplo — perfil de cliente
{
  _id: ObjectId("665f1a2b9e1c4a3d7f8b0e21"),
  nome: "Ana Souza",
  email: "ana@exemplo.com",
  criadoEm: ISODate("2026-01-15T10:00:00Z"),
  enderecos: [
    { cidade: "São Paulo", cep: "01310-000", principal: true }
  ],
  saldoCarteira: NumberDecimal("149.90")
}
Coleções e schema flexívelBásico
Coleções vs. tabelas relacionais

Uma coleção agrupa documentos, assim como uma tabela agrupa linhas num banco relacional. A diferença essencial: uma coleção não exige que todo documento tenha as mesmas colunas — dois documentos na mesma coleção podem ter estruturas diferentes.

TeoriaIsso não significa "sem schema" — significa schema flexível, aplicado na aplicação em vez de imposto pelo banco por padrão. O MongoDB oferece schema validation opcional via $jsonSchema na criação da coleção, para quem quer as garantias de um schema relacional sem abrir mão da flexibilidade de documentos aninhados. Na prática, a maioria dos documentos de uma coleção bem modelada converge para uma forma parecida — a flexibilidade existe para absorver evolução (um campo novo, uma versão antiga de registro) sem migração custosa de tabela inteira.
Dia a diaUm catálogo de produtos onde "roupa" tem campo tamanho e "eletrônico" tem campo voltagem convivem na mesma coleção produtos, sem precisar de tabelas separadas ou colunas nulas em massa.
mongosh
// Criar coleção com validação de schema (opcional)
db.createCollection("produtos", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["nome", "preco"],
      properties: {
        nome: { bsonType: "string" },
        preco: { bsonType: "decimal", minimum: 0 }
      }
    }
  }
})
_id e ObjectIdBásico
A chave primária de todo documento

Todo documento precisa de um campo _id único dentro da coleção — é a chave primária. Se você não fornecer um, o driver gera automaticamente um ObjectId.

TeoriaUm ObjectId tem 12 bytes: 4 bytes de timestamp Unix, 5 bytes de valor aleatório único por processo, e 3 bytes de contador incremental. Isso tem duas consequências práticas importantes: (1) ObjectIds gerados em sequência são quase ordenados por tempo de criação — dá para extrair a data de criação direto do próprio _id com .getTimestamp(), sem campo extra; (2) como o _id é sempre indexado por padrão (índice único), ele também funciona como uma chave de paginação eficiente por ordem de inserção.
Dia a diaUm sistema de tickets de suporte pode usar o próprio _id do ticket para saber quando ele foi aberto, sem manter um campo criadoEm redundante — ObjectId(id).getTimestamp() resolve.
mongosh
const id = ObjectId()
print(id)                    // 665f1a2b9e1c4a3d7f8b0e21
print(id.getTimestamp())     // data/hora de criação embutida no _id

// _id customizado — útil quando você já tem uma chave natural
db.usuarios.insertOne({ _id: "ana@exemplo.com", nome: "Ana Souza" })
CRUD básicoBásico
insert, find, update, delete

As quatro operações básicas seguem o padrão insertOne/insertMany, find/findOne, updateOne/updateMany e deleteOne/deleteMany — a distinção singular/plural é explícita no nome do método, ao contrário do SQL.

TeoriaUpdate no MongoDB é feito com operadores de atualização ($set, $inc, $push, $unset) que modificam campos específicos in place, em vez de substituir o documento inteiro — isso é o que permite, por exemplo, incrementar um contador atomicamente sem ler o valor antes. Passar um documento sem operadores para updateOne é um erro comum: sem $set, o MongoDB interpreta como um documento de substituição total.
⚠️Cuidado: updateMany sem filtro ({}) afeta a coleção inteira. Sempre teste o filtro primeiro com find() antes de rodar o update.
mongosh
// Create
db.pedidos.insertOne({ cliente: "Ana", total: 149.90, status: "pendente" })

// Read
db.pedidos.find({ status: "pendente" }).sort({ total: -1 }).limit(10)

// Update — operadores, não substituição total
db.pedidos.updateOne(
  { _id: pedidoId },
  { $set: { status: "enviado" }, $inc: { versao: 1 } }
)

// Delete
db.pedidos.deleteMany({ status: "cancelado", criadoEm: { $lt: dataLimite } })
02

Arquitetura Interna

Como o MongoDB guarda, replica e distribui dados

Storage Engine — WiredTigerIntermediário
Como os documentos chegam ao disco

Desde a versão 3.2, WiredTiger é o storage engine padrão do MongoDB — a camada que decide como documentos e índices são de fato organizados em disco e em memória.

TeoriaWiredTiger usa MVCC (Multi-Version Concurrency Control): cada operação enxerga uma "fotografia" (snapshot) consistente dos dados no início da operação, o que permite leituras e escritas concorrentes sem bloqueio de página. Documentos são comprimidos em disco (snappy por padrão, zstd ou zlib como alternativas) e mantidos como B-trees. Para durabilidade, o WiredTiger faz checkpoints a cada 60 segundos (todo o dataset consistente gravado em disco) e mantém um journal write-ahead que grava a cada 100ms — é o journal que permite recuperar as escritas ocorridas entre dois checkpoints em caso de queda do processo.
💡Onde importa: entender o journal explica por que { j: true } num write concern garante durabilidade mesmo sem esperar o próximo checkpoint completo.
mongosh
// Ver o storage engine ativo e estatísticas de cache
db.serverStatus().storageEngine
db.serverStatus().wiredTiger.cache
Replica SetsIntermediário
Alta disponibilidade: primary, secondary e eleições

Um replica set é um grupo de processos mongod que mantêm a mesma cópia dos dados. Um nó é eleito primary e recebe todas as escritas; os demais são secondaries, que replicam as mudanças de forma assíncrona a partir do oplog do primary.

TeoriaO oplog (operations log) é uma coleção capped especial que registra, em ordem, toda operação que modifica dados no primary; secondaries fazem "tailing" contínuo desse oplog e reaplicam as operações localmente. Se o primary fica inacessível, os secondaries elegíveis disparam uma eleição (protocolo baseado em Raft) e escolhem um novo primary em segundos — tipicamente entre 2 e 12 segundos de indisponibilidade de escrita, configurável via electionTimeoutMillis. Um replica set de produção precisa de número ímpar de membros votantes (mínimo 3) para sempre haver maioria possível numa eleição; um arbiter pode votar sem guardar dados, útil para desempate com custo mínimo.
Dia a diaSe o servidor primary de um sistema de checkout cai à meia-noite, o replica set elege um novo primary automaticamente — o time de plantão não precisa promover um servidor manualmente às 3h da manhã.
mongosh
// Inicializar um replica set de 3 nós
rs.initiate({
  _id: "rsProducao",
  members: [
    { _id: 0, host: "mongo1:27017" },
    { _id: 1, host: "mongo2:27017" },
    { _id: 2, host: "mongo3:27017" }
  ]
})

rs.status()   // mostra quem é primary, secondaries e o estado de replicação
ShardingAvançado
Escalabilidade horizontal e a escolha da shard key

Sharding particiona uma coleção horizontalmente entre múltiplos replica sets (cada um chamado shard), permitindo que o dataset e a carga de escrita cresçam além do que uma única máquina suportaria.

TeoriaUm cluster com sharding tem três peças: os shards (cada um um replica set com um pedaço dos dados), os config servers (guardam os metadados de qual faixa de dados vive em qual shard) e o mongos (roteador sem estado que a aplicação de fato acessa, e que decide para qual shard encaminhar cada query). A decisão mais importante — e mais difícil de reverter — é a shard key: o campo (ou campos) usado para decidir em qual shard cada documento vive. Uma shard key de baixa cardinalidade ou monotonicamente crescente (como um timestamp) concentra escritas num único shard, criando um "hot shard"; por isso o MongoDB oferece hashed sharding, que distribui a mesma shard key de forma pseudoaleatória entre shards.
⚠️Cuidado: a shard key não pode ser trocada depois que a coleção tem dados em múltiplos shards sem um processo de resharding — escolha com cuidado, pensando no padrão de escrita e nas queries mais frequentes.
Dia a diaUma plataforma de IoT que recebe milhões de leituras de sensores por dia distribui os dados por deviceId hashed, para que os sensores não concentrem escrita num único shard mesmo com milhares de dispositivos ativos simultaneamente.
mongosh
// Habilitar sharding no banco e na coleção (via mongos)
sh.enableSharding("iot")

// Hashed sharding — distribui escrita uniformemente entre shards
sh.shardCollection("iot.leituras", { deviceId: "hashed" })

sh.status()   // mostra chunks e distribuição entre shards
03

Consistência & Operação

Durabilidade, transações e como conectar de verdade

Read & Write ConcernsAvançado
O trade-off entre durabilidade e latência

Write concern define quantos membros do replica set precisam confirmar uma escrita antes dela ser considerada "bem-sucedida". Read concern define qual nível de consistência uma leitura exige dos dados retornados. Nenhum dos dois é fixo por padrão — ambos são escolhas explícitas por operação.

Teoria{ w: "majority" } só confirma a escrita depois que a maioria dos nós votantes gravou o dado — isso garante que a escrita sobrevive a uma eleição de primary, ao custo de esperar a replicação. { w: 1 } (padrão) confirma assim que o primary grava, mais rápido mas com risco de perda em failover antes da replicação. Do lado da leitura, readConcern: "majority" só retorna dados já confirmados pela maioria — evita ler um valor que pode ser revertido (rollback) num failover; "local" (padrão) lê o estado atual do nó, que pode ainda não ter alcançado a maioria.
💡Onde usar majority: saldo de carteira, confirmação de pagamento, qualquer escrita onde perder o dado num failover é inaceitável. Para métricas e logs de alto volume, w: 1 costuma ser aceitável.
mongosh
// Escrita crítica — espera confirmação da maioria + journal
db.pagamentos.insertOne(
  { pedidoId: "p123", valor: 149.90, status: "confirmado" },
  { writeConcern: { w: "majority", j: true } }
)

// Leitura que exige dados já confirmados pela maioria
db.pagamentos.find({ pedidoId: "p123" }).readConcern("majority")
Transações multi-documentoAvançado
ACID quando um documento não basta

Uma escrita em um único documento já é atômica por padrão no MongoDB — inclusive atualizando vários campos aninhados de uma vez. Quando a operação precisa mudar vários documentos, várias coleções ou vários bancos de forma tudo-ou-nada, existem as transações ACID explícitas.

TeoriaTransações multi-documento existem desde a versão 4.0 (replica sets) e 4.2 (sharded clusters), e seguem semântica de snapshot isolation: todas as leituras dentro da transação enxergam o mesmo ponto no tempo, e nenhuma escrita é visível fora da transação até o commit. O custo é real: transações longas seguram recursos (locks e histórico de versões no WiredTiger) por mais tempo, e o guia oficial recomenda mantê-las curtas e evitar, sempre que possível, resolver com um bom design de documento o que poderia virar uma transação entre múltiplos documentos.
⚠️Regra prática: primeiro pergunte se o modelo de dados pode evitar a transação (embedding). Só use transação multi-documento quando a operação de fato precisa ser atômica entre documentos/coleções diferentes — ex: transferência entre duas contas.
mongosh
const session = db.getMongo().startSession()
session.startTransaction({ readConcern: { level: "snapshot" }, writeConcern: { w: "majority" } })

try {
  const contas = session.getDatabase("banco").contas
  contas.updateOne({ _id: "contaA" }, { $inc: { saldo: -100 } })
  contas.updateOne({ _id: "contaB" }, { $inc: { saldo: 100 } })
  session.commitTransaction()
} catch (erro) {
  session.abortTransaction()
  throw erro
} finally {
  session.endSession()
}
Drivers, mongosh e connection stringBásico
Como a aplicação de fato se conecta

O mongosh é o shell interativo oficial (substituiu o antigo mongo). Para aplicações, o MongoDB mantém drivers oficiais para as principais linguagens (Node.js, Python, Java, Go, C#, entre outros), todos falando o mesmo protocolo de wire e configurados pela mesma connection string.

TeoriaA connection string no formato mongodb+srv:// (usada pelo Atlas) resolve os hosts do replica set via um registro DNS SRV em vez de listar cada host manualmente — o driver descobre os membros do cluster, incluindo mudanças de topologia (failover, adição de nós), sem precisar de uma nova connection string. Todo driver oficial implementa o mesmo Server Discovery and Monitoring (SDAM): monitora continuamente o estado de cada nó do cluster e direciona cada operação para o nó correto (primary para escrita, secondary elegível conforme a readPreference).
Dia a diaUma API Node.js em produção lê relatórios pesados de um secondary (readPreference=secondaryPreferred) para não competir por recursos com as escritas no primary, sem precisar de nenhuma lógica extra na aplicação — é uma opção da connection string.
bash
# Connection string típica do Atlas
mongodb+srv://usuario:senha@cluster0.abcde.mongodb.net/meubanco?retryWrites=true&w=majority&readPreference=secondaryPreferred

# mongosh
mongosh "mongodb+srv://cluster0.abcde.mongodb.net/meubanco" --username usuario