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.
mongosh (shell oficial) ou o MongoDB Compass (GUI) para explorar dados visualmente.explain("executionStats") antes de otimizar às cegasFundamentos
O modelo de dados orientado a documentos
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.
// 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") }
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.
$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.tamanho e "eletrônico" tem campo voltagem convivem na mesma coleção produtos, sem precisar de tabelas separadas ou colunas nulas em massa.// 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 } } } } })
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.
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._id do ticket para saber quando ele foi aberto, sem manter um campo criadoEm redundante — ObjectId(id).getTimestamp() resolve.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" })
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.
$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.updateMany sem filtro ({}) afeta a coleção inteira. Sempre teste o filtro primeiro com find() antes de rodar o update.// 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 } })
Arquitetura Interna
Como o MongoDB guarda, replica e distribui dados
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.
{ j: true } num write concern garante durabilidade mesmo sem esperar o próximo checkpoint completo.// Ver o storage engine ativo e estatísticas de cache db.serverStatus().storageEngine db.serverStatus().wiredTiger.cache
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.
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.// 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
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.
deviceId hashed, para que os sensores não concentrem escrita num único shard mesmo com milhares de dispositivos ativos simultaneamente.// 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
Consistência & Operação
Durabilidade, transações e como conectar de verdade
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.
{ 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.w: 1 costuma ser aceitável.// 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")
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.
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() }
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.
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).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.# 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