MongoDB: Inserção de documentos
Última atualização: 2026-08-26
A inserção de documentos é o primeiro passo para gravar dados no MongoDB — dominar insertOne e insertMany é a base da manipulação de dados.
Este curso oferece uma análise aprofundada de vários métodos para inserção de documentos, tratamento de erros, otimização de desempenho e o mecanismo Write Concern.
1. O que você vai aprender
- Principais usos de
insertOneeinsertMany - A opção “ordenada” e o comportamento da inserção em lote
- Gravação com preocupação (w, j, wtimeout)
- Estratégias para lidar com conflitos de _id
- Otimizações de desempenho para inserções em massa
- Inserir tipos especiais, como data e ObjectId
- Solução de problemas comuns relacionados à inserção
2. A história real de um engenheiro de dados
(1) Problema: As importações em lote de 1 milhão de registros costumam falhar
Alice é engenheira de dados em uma empresa de comércio eletrônico e precisa migrar 1 milhão de registros de produtos do MySQL para o MongoDB:
“Usei o
insertManypara importar 1 milhão de registros de produtos, mas toda a importação falhou no 500.000º registro devido a um _id duplicado, o que me fez perder quatro horas. É tudo ou nada — isso é um desastre para uma migração incremental.”
Os problemas que ela enfrenta:
| Questão | Impacto |
|---|---|
| ordenado: true (padrão) | Se um item do lote falhar, todo o lote será considerado reprovado |
| Ausência de preocupação com gravação | Perda de dados devido a falha no servidor |
| _conflito de ID | Falha devido à duplicação de IDs autoincrementais no MySQL |
| Inserção em massa | 1 milhão de inserções individuais levaram 1 hora |
(2) Uma solução utilizando o MongoDB e bulkWrite
// === Usage bulkWrite + ordered: false Resolving Partial Failure Issues ===
const products = [...]; // 100,000 product records
const BATCH_SIZE = 1000;
for (let i = 0; i < products.length; i += BATCH_SIZE) {
const batch = products.slice(i, i + BATCH_SIZE);
try {
await Product.bulkWrite(
batch.map(doc => ({
insertOne: { document: doc }
})),
{ ordered: false } // Allow for Partial Failure,Continue execution
);
} catch (err) {
console.error(`Batch ${i / BATCH_SIZE} Failure:${err.writeErrors?.length} items`);
}
}
// === Usage Write Concern Ensure Data Persistence ===
await Product.bulkWrite(
batch.map(doc => ({ insertOne: { document: doc } })),
{
ordered: false,
writeConcern: { w: 'majority', j: true, wtimeout: 5000 }
}
);
(3) Receita
| Dimensão | Inserção em uma única linha | Inserção em massa + ordenada: falso |
|---|---|---|
| Desempenho | 1 milhão de registros ~60 minutos | 1 milhão de registros ~3 minutos |
| Tolerância a falhas | Uma única falha resulta em perda | Uma falha parcial permite a continuação |
| Segurança de dados | Suscetível a perdas | Persistência com preocupação de gravação |
| Complexidade do código | Simples | Moderada |
3. insertOne: Inserir um único documento
Explicação do conceito: insertOne é o método de gravação mais básico no MongoDB, utilizado para inserir um documento em uma coleção. Cada documento no MongoDB é armazenado no formato BSON e recebe automaticamente uma chave primária _id exclusiva. Ao contrário do INSERT INTO em bancos de dados relacionais, o insertOne não requer uma estrutura de tabela predefinida, e os documentos podem conter qualquer combinação de campos.
Como funciona: Quando um cliente inicia uma solicitação insertOne, o servidor MongoDB executa as seguintes etapas: valida o formato BSON → verifica a exclusividade de _id → grava no mecanismo de armazenamento WiredTiger → aplica a Write Concern → retorna insertedId. Todo o processo de gravação é atômico para um único documento.
sequenceDiagram
participant App as Applications
participant Mongod as MongoDB Server-side
participant WT as WiredTiger Engine
App->>Mongod: insertOne({ doc })
Mongod->>Mongod: Verification BSON Format
Mongod->>Mongod: Inspection _id Unique Index
alt _id Conflict
Mongod-->>App: E11000 duplicate key error
else _id The Only One
Mongod->>WT: Write to the document + Update Index
WT-->>Mongod: Confirm Write
Mongod-->>App: { acknowledged: true, insertedId }
end
| Parâmetro | Tipo | Descrição |
|---|---|---|
document |
Documento | Documento a ser inserido (obrigatório) |
writeConcern |
Documento | Nível de confirmação de gravação (opcional) |
| Cenários aplicáveis | Cenários não aplicáveis |
|---|---|
| Criação de um único registro (cadastro de usuário) | Importação em massa de dados |
É necessário recuperar insertedId |
Gravação em massa de mais de 1.000 registros |
| Documentos com aninhamento complexo | Importação com remoção de dados duplicados |
(1) Sintaxe básica
// === insertOne Basic Usage ===
db.products.insertOne({
sku: "PHONE-001",
title: "Smartphone X",
price: NumberDecimal("599.99"),
category: "Electronics",
stock: 50,
createdAt: new Date()
});
// Return Results:
// {
// acknowledged: true,
// insertedId: ObjectId('507f1f77bcf86cd799439011')
// }
Análise dos pontos-chave:
acknowledged: trueindica que a operação de gravação foi confirmada pelo servidor MongoDB (sujeito à Write Concern)- Se
writeConcern: { w: 0 }, entãoacknowledgedéfalse, einsertedIdnão é retornado. - O valor de
insertedIddepende de_idter sido especificado manualmente; caso contrário, um ObjectId é gerado automaticamente.
(2) Análise dos valores de retorno
Explicação do conceito: O valor de retorno de insertOne contém dois campos-chave — acknowledged e insertedId. Se acknowledged for true, isso indica que a operação de gravação foi confirmada pelo servidor MongoDB (sujeito à Write Concern; se for w: 0, então será false). .insertedId é o valor _id do documento inserido; ele é retornado independentemente de _id ter sido gerado automaticamente ou especificado manualmente.
| Campo de retorno | Tipo | Descrição | Observações |
|---|---|---|---|
acknowledged |
Booleano | Indica se a gravação foi confirmada | falso quando w: 0 |
insertedId |
ObjectId/Any | O _id do documento inserido | Retorna o valor especificado quando definido manualmente |
const result = db.products.insertOne({
sku: "TEST-001",
title: "Test Product"
});
print(result.acknowledged); // true(Write confirmed)
print(result.insertedId); // ObjectId('507f1f77bcf86cd799439012')
| Campo | Tipo | Descrição |
|---|---|---|
acknowledged |
booleano | “true” indica que a gravação foi confirmada |
insertedId |
ObjectId | O _id do documento inserido |
(3) O _id é gerado automaticamente
Explicação do conceito: _id é a chave primária de um documento do MongoDB, com tipo padrão ObjectId (binário de 12 bytes). Se _id não for especificado manualmente, o driver do MongoDB o gera automaticamente no lado do cliente, garantindo que seja único antes de ser gravado no servidor. Esse design difere dos IDs de autoincremento do MySQL — o ObjectId não depende de um contador centralizado e, por natureza, é compatível com ambientes distribuídos.
Como funciona: Um ObjectId é composto por um timestamp de 4 bytes + um valor aleatório de 5 bytes (máquina + processo) + um contador incremental de 3 bytes. A parte do timestamp garante que os ObjectIds sejam ordenados naturalmente por data e hora de inserção; o valor aleatório garante a exclusividade entre processos; e o contador garante a exclusividade dentro de um único segundo.
graph LR
A[Client-Side Generation ObjectId] --> B[Timestamp 4B<br/>Insertion Time]
A --> C[Random value 5B<br/>Machine+Unique Process]
A --> D[Counter 3B<br/>Increment within the same second]
B --> E[Globally Unique<br/>Naturally Ordered<br/>Withdrawal Time]
| _id Estratégia | Exemplo | Cenários aplicáveis | Ordenação |
|---|---|---|---|
| Auto ObjectId | ObjectId("...") |
Cenários gerais (padrão) | ✅ Ordenar por hora |
| Chave comercial de string | "ORDER-2026-001" |
Número do pedido, SKU | Depende do formato |
| Autoincremento | NumberInt(1) |
Migração de sistemas legados | ✅ Ordenar por número |
| Carimbo de data/hora | NumberLong(1700000000) |
Dados de séries temporais | ✅ Classificados por hora |
| UUID | UUID("...") |
Único entre sistemas | ❌ Sem ordem |
// === Not specified _id(Automatically Generated ObjectId)===
db.users.insertOne({
name: "Alice",
email: "alice@example.com"
});
// Automatically Generated _id: ObjectId('507f1f77bcf86cd799439011')
// === Specify manually _id ===
db.users.insertOne({
_id: "user_001", // String ID
name: "Alice"
});
db.users.insertOne({
_id: ObjectId(), // Generated Manually ObjectId
name: "Bob"
});
db.users.insertOne({
_id: NumberLong(1700000000000), // Timestamps as ID
name: "Charlie"
});
(4) Exclusividade do _id
Explicação do conceito: O MongoDB cria automaticamente um índice único no campo _id de cada coleção, o que serve como garantia fundamental da integridade dos dados. A característica exclusiva do índice único _id é que ele não pode ser excluído — mesmo que dropIndexes() seja executado, o índice _id permanece. Quando um documento com um _id duplicado é inserido, o MongoDB gera um erro E11000 duplicate key error, e toda a operação de inserção é revertida.
Casos de uso: Em cenários de migração de dados e importação em massa, os conflitos de _id são a fonte mais comum de erros. Entender como prevenir e resolver esses conflitos é fundamental para garantir uma operação estável em ambientes de produção. A estratégia de prevenção recomendada é consultar o conjunto existente de _id antes da importação ou utilizar o esquema upsert como alternativa ao insertOne.
| Cenário | Causa do conflito | Estratégia recomendada |
|---|---|---|
| Migração do MySQL para o MongoDB | Conflito entre IDs autoincrementais e dados existentes | Remova o antigo _id e deixe o MongoDB gerá-lo |
| Combinação de dados de várias fontes | Diferentes fontes de dados possuem a mesma chave de negócios | Adicionar prefixo: sourceA_ORDER-001 |
| Sincronização incremental | Os dados de origem já existem no destino | updateOne + upsert |
| Importação em massa | CSV/JSON com linhas duplicadas | ordered: false Ignorar duplicatas |
// === _id Handling Repeated Errors ===
try {
db.users.insertOne({
_id: "user_001", // Already exists
name: "Alice Duplicate"
});
} catch (err) {
// E11000 duplicate key error collection: shopdb.users index: _id_
print("❌ _id Already exists:" + err.message);
}
// === Usage upsert Handling Duplicates ===
db.users.updateOne(
{ _id: "user_001" },
{ $set: { name: "Alice Updated" } },
{ upsert: true } // If it doesn't exist, insert it,If it exists, update it
);
▶ Exemplo 1: Uso completo de insertOne
// === Inserting Different Types of Fields ===
db.products.insertOne({
// String
sku: "PHONE-X-256-BLK",
title: "Smartphone X 256GB Black",
// Numeric Types
price: NumberDecimal("599.99"), // Decimal128(Accurate)
stock: NumberInt(50), // Int32
viewCount: NumberLong(1000000), // Long
// Boolean
isActive: true,
isFeatured: false,
// Date
createdAt: new Date(),
releaseDate: ISODate("2026-01-01"),
// Array
tags: ["5g", "amoled", "fast-charging"],
colors: ["Black", "White", "Blue"],
// Nested Documents
specs: {
screen: "6.5 inch OLED",
battery: "4500mAh",
camera: "108MP"
},
// Binary
thumbnail: BinData(0, "iVBORw0KGgoAAAANSUhEUgAA..."),
// Null
discount: null
});
▶ Exemplo 2: insertOne com diferentes estratégias _id
// === Strategy 1:Automatic ObjectId(Default)===
const r1 = db.users.insertOne({ name: "Alice", email: "alice@example.com" });
print(`Auto ObjectId: ${r1.insertedId}`);
// === Strategy 2:String Business Key ===
const r2 = db.orders.insertOne({
_id: "ORD-20260701-0001",
total: NumberDecimal("599.99"),
status: "pending"
});
print(`Business key: ${r2.insertedId}`);
// === Strategy 3:Nested Documents + Array ===
const r3 = db.products.insertOne({
_id: ObjectId(),
sku: "PHONE-X-256-BLK",
specs: { screen: "6.5 inch OLED", battery: "4500mAh" },
tags: ["5g", "amoled"],
price: NumberDecimal("599.99")
});
print(`Nested doc: ${r3.insertedId}`);
Saída: Auto ObjectId: ObjectId('...') | Chave de negócios: ORD-20260701-0001 | Documento aninhado: ObjectId('...')
4. insertMany: Inserção em lote
Descrição do conceito: insertMany — inserção de vários documentos em uma única operação — é o método principal para gravação de dados em lote. Em comparação com o método insertOne, que processa os documentos um por um, o insertMany agrupa vários documentos em uma única solicitação de rede enviada ao servidor, reduzindo significativamente a sobrecarga de ida e volta da rede e melhorando o desempenho em 10 a 100 vezes.
Como funciona: insertMany recebe uma matriz de documentos e determina a estratégia de execução com base na opção ordered. ordered: true (padrão) insere os itens sequencialmente, um por um, e interrompe imediatamente ao encontrar um erro; ordered: false permite a inserção paralela e ignora os itens com falha para continuar a execução. Essas duas estratégias têm um impacto significativo no desempenho e na integridade dos dados.
graph TB
A[insertMany<br/>1000 Documents] --> B{ordered option}
B -->|ordered: true| C[Sequential Insertion<br/>Item 1 -> Item 2 -> ...<br/>Stop on Error]
B -->|ordered: false| D[Parallel Insertion<br/>Writing Multiple Rows Simultaneously<br/>Skip failed items]
C --> C1[Performance:Intermediate<br/>Consistency:Strong]
D --> D1[Performance:Higher<br/>Consistency:Weak]
style D fill:#d4edda
| Parâmetro | Tipo | Descrição |
|---|---|---|
documents |
Matriz | Matriz de documentos (obrigatório, pelo menos 1 entrada) |
ordered |
Booleano | true Execução sequencial (padrão), false Execução paralela |
writeConcern |
Documento | Nível de confirmação de gravação |
| Cenários aplicáveis | Cenários não aplicáveis |
|---|---|
| Migração de dados, importação em massa | Inserção de um único documento |
| Geração de dados de teste | Gravações que exigem ordenação estrita de transações |
| Gravação de logs em lote | Fortes dependências entre documentos |
(1) Sintaxe básica
// === insertMany Basic Usage ===
db.products.insertMany([
{ sku: "PHONE-001", title: "Phone A", price: 599.99 },
{ sku: "PHONE-002", title: "Phone B", price: 699.99 },
{ sku: "PHONE-003", title: "Phone C", price: 799.99 }
]);
// Return Results:
// {
// acknowledged: true,
// insertedIds: {
// '0': ObjectId('507f1f77bcf86cd799439011'),
// '1': ObjectId('507f1f77bcf86cd799439012'),
// '2': ObjectId('507f1f77bcf86cd799439013')
// },
// insertedCount: 3
// }
(2) A opção “ordenada” (fundamental!)
Explicação do conceito: ordered é a opção mais importante para insertMany. Ela determina como o MongoDB lida com erros durante gravações em lote — se deve interromper imediatamente ou ignorar o erro e continuar. Compreender ordered é fundamental para a importação de dados em ambientes de produção.
Casos de uso: Para cenários de migração de dados e sincronização incremental, recomenda-se ordered: false, pois os dados de origem podem conter duplicatas _id; ignorar as duplicatas e continuar a importação é mais razoável do que ter todo o lote reprovado. Para cenários de transações financeiras, recomenda-se ordered: true para garantir um sequenciamento operacional rigoroso.
// === ordered: true(Default)— If it fails in the middle, stop ===
db.products.insertMany([
{ _id: 1, sku: "A" },
{ _id: 2, sku: "B" },
{ _id: 1, sku: "C" }, // ❌ _id Conflict
{ _id: 4, sku: "D" } // ⚠️ Will not be inserted(Previous failure)
]);
// Error:E11000 duplicate key error
// Actual insertion:A, B(2 items),C and D Not inserted
// === ordered: false — Skip failure,Continue execution ===
db.products.insertMany([
{ _id: 1, sku: "A" },
{ _id: 2, sku: "B" },
{ _id: 1, sku: "C" }, // ❌ _id Conflict
{ _id: 4, sku: "D" } // ✅ Still inserted
], { ordered: false });
// The error message lists all documents that failed to be indexed:
// BulkWriteError: 1 document(s) failed
// writeErrors: [
// { index: 2, code: 11000, errmsg: 'duplicate key' }
// ]
// Actual insertion:A, B, D(3 items),C Not inserted
(3) Comparação das opções “ordenadas”
| Dimensão | ordenado: verdadeiro | ordenado: falso |
|---|---|---|
| Tipo de falha | Falha em todo o lote | Ignorar a falha e continuar |
| Desempenho | Moderado | Mais rápido (paralelo) |
| Casos de uso | Consistência forte (por exemplo, transferências de fundos) | Importação incremental, registros |
| Mensagem de erro | Primeiro item com falha | Detalhes de todas as falhas |
▶ Exemplo 3: Um guia completo sobre inserção em lote
// === Example of Batch Importing E-commerce Products ===
const products = [
{ sku: "LAPTOP-001", title: "Laptop Pro", price: NumberDecimal("1299.99"), category: "Electronics", stock: 20 },
{ sku: "LAPTOP-002", title: "Laptop Air", price: NumberDecimal("999.99"), category: "Electronics", stock: 30 },
{ sku: "PHONE-001", title: "Smartphone X", price: NumberDecimal("599.99"), category: "Electronics", stock: 50 },
{ sku: "BOOK-001", title: "JavaScript Guide", price: NumberDecimal("29.99"), category: "Books", stock: 200 },
{ sku: "BOOK-002", title: "MongoDB Tutorial", price: NumberDecimal("34.99"), category: "Books", stock: 150 }
];
// === Default Mode(Orderly)===
try {
const result = db.products.insertMany(products);
print(`✅ Insert ${result.insertedCount} Items`);
} catch (err) {
print(`❌ Batch Failure:${err.message}`);
}
// === Fault-Tolerant Mode(Disorder)===
try {
const result = db.products.insertMany(products, { ordered: false });
print(`✅ Insert ${result.insertedCount} Items`);
} catch (err) {
print(`⚠️ Partial failure:Success ${err.result.insertedCount} items,Failure ${err.writeErrors.length} items`);
err.writeErrors.forEach(e => print(` Index of Failures ${e.index}: ${e.errmsg}`));
}
5. Preocupação com a redação
Explicação do conceito: O Write Concern é um mecanismo de segurança de gravação no MongoDB que define “quando uma operação de gravação é considerada bem-sucedida”. Ele controla o número de nós de réplica que devem confirmar a operação de gravação antes que uma resposta seja retornada ao cliente. Isso representa um equilíbrio fundamental entre a durabilidade dos dados e o desempenho de gravação — quanto maior o nível de confirmação, maior a segurança, mas maior também a latência.
Como funciona: Em uma arquitetura de conjunto de réplicas, as operações de gravação chegam primeiro ao nó primário e, em seguida, são replicadas de forma assíncrona para os nós secundários. O parâmetro w da Write Concern determina quantas confirmações de nó devem ser aguardadas. w: 1 aguarda apenas a confirmação do nó primário (é a opção mais rápida, mas apresenta risco de perda de dados), w: "majority" aguarda a confirmação da maioria dos nós (recomendado para ambientes de produção) e j: true garante que os dados tenham sido gravados no diário do disco.
sequenceDiagram
participant App as Client
participant P as Primary
participant S1 as Secondary 1
participant S2 as Secondary 2
App->>P: insertOne({ doc }, { w: "majority" })
P->>P: Write to memory + Journal
P->>S1: Copy oplog
P->>S2: Copy oplog
S1-->>P: Confirm Write
S2-->>P: Confirm Write
Note over P: majority Reached(2/3 Node)
P-->>App: { acknowledged: true }
| Dimensão | w: 0 | w: 1 | w: maioria | w: maioria + j: verdadeiro |
|---|---|---|---|---|
| Número de nós de confirmação | Sem espera | Primário | Maioria dos nós | Maioria dos nós + disco |
| Desempenho | Mais rápido | Rápido | Médio | Lento |
| Segurança de dados | Possível perda | Os dados podem ser perdidos se o servidor principal ficar fora do ar | Praticamente nenhuma perda | O mais seguro |
| Cenários recomendados | Registro | Desenvolvimento | Produção | Finanças |
(1) O que é “Write Concern”?
O Write Concern descreve o nível de confirmação necessário para que uma operação de gravação seja considerada bem-sucedida e determina quando os dados são considerados “salvos”.
graph LR
A[Client] -->|insertOne| B[mongod Receive]
B --> C{Write Concern Layout}
C -->|w: 1| D[Primary Write and Return]
C -->|w: majority| E[After most nodes have confirmed, return]
C -->|j: true| F[Returns only after writing to disk]
style E fill:#d4edda
style F fill:#d4edda
(2) Parâmetros de preocupação de gravação
| Parâmetro | Valor | Descrição |
|---|---|---|
w |
0 / 1 / "majority" / Número |
Número de nós com confirmação de gravação |
j |
true / false |
Gravar o diário no disco |
wtimeout |
Número de milissegundos | Tempo limite (padrão: esperar indefinidamente) |
(3) Comparação dos níveis de preocupação com gravação
// === w: 0 — Do not wait for confirmation(Fastest,May be missing)===
db.products.insertOne(
{ sku: "TEST-001", title: "Test" },
{ writeConcern: { w: 0 } }
);
// Return Now,Write success is not guaranteed
// === w: 1 — Primary Node Confirmation(Default)===
db.products.insertOne(
{ sku: "TEST-002", title: "Test" },
{ writeConcern: { w: 1 } }
);
// Primary Write and Return
// === w: "majority" — Confirmed by a majority of nodes(Safest)===
db.products.insertOne(
{ sku: "TEST-003", title: "Test" },
{ writeConcern: { w: "majority", j: true, wtimeout: 5000 } }
);
// The replica waits until most nodes have written to disk before returning(Recommended Production Environment)
(4) Comparação das configurações de Write Concern
| Nível | Desempenho | Segurança de dados | Casos de uso |
|---|---|---|---|
w: 0 |
⚡⚡⚡ Extremamente rápido | ❌ Suscetível a perdas | Registros, dados temporários |
w: 1 |
⚡⚡ Rápido | ⚠️ Pode se perder | Desenvolvimento independente |
w: majority |
⚡ Médio | ✅ Praticamente sem perda | Recomendado para ambientes de produção |
w: majority, j: true |
⚠️ Mais lento | ✅✅ Mais seguro | Finanças, dados críticos |
▶ Exemplo 4: Configuração de Write Concern em ambiente de produção
// === Cluster-Level Settings(Recommendations)===
db.adminCommand({
setDefaultRWConcern: 1,
defaultWriteConcern: { w: "majority", j: true, wtimeout: 10000 },
defaultReadConcern: { level: "majority" }
});
// === Single-Write Specification ===
db.orders.insertOne(
{ userId: "user_001", total: 599.99, items: [...] },
{ writeConcern: { w: "majority", j: true, wtimeout: 5000 } }
);
// === mongoose Settings ===
const OrderSchema = new mongoose.Schema({
userId: String,
total: mongoose.Schema.Types.Decimal128,
items: Array
}, {
writeConcern: { w: 'majority', j: true, wtimeout: 5000 }
});
6. Estratégia de resolução de conflitos de _id
Explicação do conceito: _id é o identificador exclusivo de um documento do MongoDB, e o campo _id em cada coleção cria automaticamente um índice exclusivo. Quando o _id de um documento inserido coincide com o de um documento já existente, o MongoDB gera um erro E11000 duplicate key error. Em cenários como migração de dados, importações em massa e fusão de várias fontes, os conflitos de _id são um dos problemas mais comuns.
Como funciona: Antes de gravar um documento, o MongoDB verifica primeiro se o campo _id viola a restrição do índice único. Se ocorrer um conflito, toda a operação de gravação é revertida (atomicidade de documento único), e é retornado o código de erro 11000. Compreender as diferenças entre as diversas estratégias de resolução de conflitos é fundamental para a integridade dos dados em ambientes de produção.
graph TB
A[_id Conflict E11000] --> B[Strategy Selection]
B --> C[Ignore duplicates<br/>ordered: false]
B --> D[Overwrite the old value<br/>replaceOne + upsert]
B --> E[Partial Update<br/>updateOne + upsert]
B --> F[Regenerate _id<br/>Remove _id Field]
B --> G[Retry Mechanism<br/>Application-Layer Retry]
style E fill:#d4edda
| Estratégia | Sintaxe | Integridade dos dados | Casos de uso |
|---|---|---|---|
| Ignorar duplicatas | ordered: false |
Manter dados antigos | Importação incremental, registro |
| Sobrescrever valores antigos | replaceOne + upsert |
Substituir por novos dados | Sincronização completa |
| Atualização parcial | updateOne + upsert |
Combinar dados antigos e novos | Campos de atualização incremental |
| Ignorar _id | Excluir o campo _id |
Inserir tudo (novo _id) | Importar sem duplicatas |
| Mecanismo de repetição | Repetição no nível do aplicativo | Depende do novo _id | Conflito temporário |
(1) Sintomas de erro
// === _id Repeated Mistakes ===
db.users.insertOne({ _id: 1, name: "Alice" });
// { acknowledged: true, insertedId: 1 }
db.users.insertOne({ _id: 1, name: "Bob Duplicate" });
// E11000 duplicate key error collection: shopdb.users index: _id_ dup key: { _id: 1 }
(2) 5 estratégias de gestão
graph TB
A[_id Conflict] --> B[Strategy 1<br/>Skip duplicates]
A --> C[Strategy 2<br/>Overwrite the old value]
A --> D[Strategy 3<br/>upsert Automatic Selection]
A --> E[Strategy 4<br/>Ignore _id Field]
A --> F[Strategy 5<br/>Retry Mechanism]
style D fill:#d4edda
(3) Implementação da estratégia
// === Strategy 1:Usage ordered: false Skip duplicates ===
try {
db.users.insertMany(
[{ _id: 1, name: "Alice" }, { _id: 2, name: "Bob" }, { _id: 1, name: "Dup" }],
{ ordered: false }
);
} catch (err) {
print(`Skip ${err.writeErrors.length} Duplicate entry`);
}
// === Strategy 2:Usage replaceOne Coverage ===
db.users.replaceOne(
{ _id: 1 },
{ _id: 1, name: "Alice Updated", updatedAt: new Date() },
{ upsert: true }
);
// === Strategy 3:Usage updateOne + upsert ===
db.users.updateOne(
{ _id: 1 },
{ $set: { name: "Alice", email: "alice@example.com" } },
{ upsert: true } // If it doesn't exist, insert it,If it exists, update it
);
// === Strategy 4:When inserting, make sure to MongoDB Automatically Generated _id ===
const docs = externalData.map(d => {
const { _id, ...rest } = d; // Deconstruct and remove _id
return rest; // let MongoDB automatically generate _id
});
db.users.insertMany(docs);
// === Strategy 5:Retry Mechanism(Application Layer)===
async function insertWithRetry(doc, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await db.collection('users').insertOne(doc);
} catch (err) {
if (err.code === 11000 && i < maxRetries - 1) {
// Generate a new one _id Retry
doc._id = new ObjectId();
continue;
}
throw err;
}
}
}
▶ Exemplo 5: Importação em massa + estratégia de remoção de duplicatas
// === Scene:Import CSV Data,Part _id Already exists ===
const csvData = [
{ _id: "USER-001", name: "Alice", email: "alice@example.com" },
{ _id: "USER-002", name: "Bob", email: "bob@example.com" },
{ _id: "USER-001", name: "Alice Duplicate", email: "alice2@example.com" },
{ _id: "USER-003", name: "Charlie", email: "charlie@example.com" }
];
// === Plan A:Ignore duplicates,Insert new data only ===
const insertedIds = [];
const duplicates = [];
csvData.forEach(doc => {
try {
const result = db.users.insertOne(doc);
insertedIds.push(result.insertedId);
} catch (err) {
if (err.code === 11000) {
duplicates.push(doc._id);
} else {
throw err;
}
}
});
print(`✅ Insert ${insertedIds.length} new data entries`);
print(`⚠️ Skip ${duplicates.length} Duplicate entry:${duplicates.join(', ')}`);
// === Plan B:Duplicate Coverage,Update existing data ===
db.users.bulkWrite(
csvData.map(doc => ({
replaceOne: {
filter: { _id: doc._id },
replacement: doc,
upsert: true
}
})),
{ ordered: false }
);
7. Otimização de desempenho para inserções em massa
Explicação do conceito: Os gargalos de desempenho em inserções em massa decorrem principalmente de três áreas: sobrecarga da viagem de ida e volta da rede, sobrecarga da atualização de índices e sobrecarga de E/S de disco. Ao compreender esses três gargalos e otimizá-los um por um, é possível reduzir o tempo necessário para importar 1 milhão de registros de 60 minutos para menos de 3 minutos.
Como funciona: O processamento de documentos um por um insertOne gera, a cada vez, uma solicitação de rede, uma atualização de índice e uma gravação em disco. insertMany combina vários documentos em uma única solicitação de rede, reduzindo a sobrecarga em dois terços. bulkWrite Além disso, suporta tipos de operações mistas (inserção + atualização + exclusão), concluindo-as em uma única solicitação. Remover temporariamente índices desnecessários antes da importação pode melhorar ainda mais o desempenho em 5 a 10 vezes.
graph LR
A[100,000 data entries] --> B[item by item insertOne<br/>~60 minutes<br/>100,000 web requests]
A --> C[insertMany 1000/batch<br/>~5 minutes<br/>1000 network requests]
A --> D[bulkWrite + dropIndexes<br/>~3 minutes<br/>1000 requests + Updates Without Indexes]
style D fill:#d4edda
| Estratégias de otimização | Melhorias de desempenho | Riscos | Cenários recomendados |
|---|---|---|---|
insertMany Substitui insertOne |
10–100x | Nenhuma | Todas as gravações em lote |
ordered: false |
1,5–3x | Pode ignorar entradas com erro | Importação tolerante a falhas |
| Excluir índices temporariamente | 5–10x | É necessário reconstruí-los após a importação | Importação inicial |
bulkWrite Substitui insertMany |
1,2–1,5x | Nenhuma | Operação mista |
w: 0 (Sem espera por confirmação) |
2–5x | Pode haver perda de dados | Dados temporários, registros |
(1) Comparação de desempenho
graph LR
A[Insert 100,000 data entries] --> B[Insert one by one<br/>~60 minutes]
A --> C[insertMany 1000/batch<br/>~5 minutes]
A --> D[bulkWrite 1000/batch<br/>~3 minutes]
style D fill:#d4edda
(2) Estratégias de otimização
// === Optimization 1:Reasonable Batch Size ===
const BATCH_SIZE = 1000; // Recommendations 500-5000
for (let i = 0; i < data.length; i += BATCH_SIZE) {
const batch = data.slice(i, i + BATCH_SIZE);
db.collection.insertMany(batch, { ordered: false });
}
// === Optimization 2:Usage bulkWrite Replace insertMany ===
await Collection.bulkWrite(
data.map(doc => ({ insertOne: { document: doc } })),
{ ordered: false }
);
// === Optimization 3:Disable Index(During the import)===
// ⚠️ Use with caution:After the import is complete, remember to rebuild the indexes.
db.products.dropIndexes();
db.products.insertMany(data);
// Rebuild Index
db.products.createIndex({ sku: 1 }, { unique: true });
// === Optimization 4:Usage Write Concern 0(Extremely fast but unsafe)===
db.products.insertMany(data, { writeConcern: { w: 0 } });
// ⚠️ For temporary data only,Not recommended for production
// === Optimization 5:Usage mongoose bulkWrite ===
const result = await Product.bulkWrite(
data.map(doc => ({
insertOne: { document: doc }
})),
{ ordered: false }
);
(3) Seleção do tamanho do lote
| Tamanho dos dados | Tamanho recomendado do lote | Motivo |
|---|---|---|
| < 100 KB | 1.000–5.000 | Baixa sobrecarga de rede |
| 100 KB - 1 MB | 500–2000 | Equilíbrio entre taxa de transferência e latência |
| > 1 MB | 100–500 | Evite solicitações individuais excessivamente grandes |
| Documento muito grande (quase 16 MB) | 1-10 | O próprio documento é grande |
▶ Exemplo 6: Script de importação de dados de alto desempenho
// === Import 100 Product Data for 10,000 Items(Optimized Version)===
const fs = require('fs');
const readline = require('readline');
const { MongoClient } = require('mongodb');
async function importLargeDataset() {
const client = new MongoClient('mongodb://localhost:27017');
await client.connect();
const collection = client.db('shopdb').collection('products');
// 1. Temporarily Delete an Index(Import Speed ↑5x)
await collection.dropIndexes().catch(() => {});
await collection.createIndex({ sku: 1 }, { unique: true }); // Preserve the unique index(Duplicate Prevention)
// 2. Streaming Read CSV
const fileStream = fs.createReadStream('products.csv');
const rl = readline.createInterface({ input: fileStream });
let buffer = [];
const BATCH_SIZE = 2000;
for await (const line of rl) {
const [sku, title, price, category] = line.split(',');
buffer.push({
sku,
title,
price: price ? NumberDecimal(price) : null,
category,
createdAt: new Date()
});
if (buffer.length >= BATCH_SIZE) {
try {
await collection.insertMany(buffer, { ordered: false });
} catch (err) {
if (err.writeErrors) {
console.warn(`⚠️ Skip ${err.writeErrors.length} Duplicate entry`);
}
}
buffer = [];
}
}
// 3. Insert the remaining data
if (buffer.length > 0) {
await collection.insertMany(buffer, { ordered: false });
}
// 4. Rebuild Index
await collection.createIndex({ category: 1, price: 1 });
await collection.createIndex({ title: 'text' });
console.log(`✅ Import Complete`);
await client.close();
}
importLargeDataset().catch(console.error);
8. Tipos especiais de inserções
Explicação do conceito: O formato BSON do MongoDB suporta uma variedade muito maior de tipos de dados do que o JSON. Ao inserir documentos, o uso correto desses tipos especiais é fundamental para evitar perda de precisão dos dados e erros de tipo. Os três problemas de precisão mais comuns são: (1) O Number do JavaScript é um número de ponto flutuante de precisão dupla, 0.1 + 0.2 ≠ 0.3; (2) o JSON não possui um tipo de data, portanto new Date() é convertido em uma string JSON.stringify(); (3) o JSON não suporta dados binários, portanto imagens e arquivos não podem ser armazenados diretamente.
Como funciona: Antes de enviar uma solicitação de inserção, o driver do MongoDB (incluindo o mongosh e o driver do Node.js) primeiro serializa o objeto JavaScript em BSON. Durante esse processo, o objeto Date é serializado como um tipo Date do BSON (carimbo de data/hora de 64 bits em milissegundos), NumberDecimal() é serializado como Decimal128 (alta precisão de 128 bits) e Buffer é serializado como BSON Binary. Compreender esse processo de serialização é fundamental para usar esses tipos especiais corretamente.
graph TB
A[JavaScript Object] --> B[Driver Serialization]
B --> C{Field Type Determination}
C -->|Date Object| D[BSON Date<br/>64-bit Millisecond timestamp]
C -->|NumberDecimal| E[BSON Decimal128<br/>128-bit High precision]
C -->|Number Constants| F[BSON Double<br/>64-bit Floating-point]
C -->|Buffer / BinData| G[BSON Binary<br/>Subtype + Byte Stream]
C -->|ObjectId| H[BSON ObjectId<br/>12 Byte]
C -->|null| I[BSON Null]
style E fill:#d4edda
style D fill:#d4edda
| Tipo | Sintaxe | Precisão/Intervalo | Cenários típicos |
|---|---|---|---|
Date |
new Date() / ISODate("...") |
Precisão em milissegundos | Carimbo de data/hora, período de validade |
Decimal128 |
NumberDecimal("0.30") |
34 dígitos decimais | Valor, cálculo preciso |
Int32 |
NumberInt(123) |
-2³¹ ~ 2³¹-1 | Contagem, Inventário |
Long |
NumberLong(1700000000) |
-2^63 ~ 2^63-1 | ID do carimbo de data/hora, inteiro grande |
BinData |
BinData(0, "base64...") |
Qualquer arquivo binário | Imagens, PDFs |
ObjectId |
ObjectId() / new ObjectId() |
12 bytes | Referência do documento, chave primária |
(1) Inserir data
// === Current Time ===
db.logs.insertOne({ event: "login", timestamp: new Date() });
// === Specified time ===
db.logs.insertOne({
event: "signup",
timestamp: ISODate("2026-07-01T10:30:00Z")
});
// === Create from a string ===
db.logs.insertOne({
event: "purchase",
timestamp: new Date("2026-07-01")
});
(2) Inserir ObjectId
// === Automatically Generated ===
db.users.insertOne({ name: "Alice" });
// === Create Manually ===
db.users.insertOne({
_id: new ObjectId(),
name: "Bob"
});
// === Create from 24-digit hex string ===
db.users.insertOne({
_id: ObjectId("507f1f77bcf86cd799439011"),
name: "Charlie"
});
// === Created from a timestamp(Used for range queries)===
const startOfDay = ObjectId.createFromTime(
Math.floor(new Date('2026-07-01').getTime() / 1000)
);
db.orders.insertOne({
_id: startOfDay,
total: 999.99
});
(3) Inserção de documentos aninhados
// === Nested Objects ===
db.products.insertOne({
sku: "PHONE-001",
specs: {
screen: { size: "6.5", type: "OLED" },
battery: { capacity: "4500mAh", type: "Li-Po" }
}
});
// === Array ===
db.products.insertOne({
sku: "SHIRT-001",
sizes: ["S", "M", "L", "XL"],
colors: [
{ name: "Red", hex: "#FF0000" },
{ name: "Blue", hex: "#0000FF" }
]
});
▶ Exemplo 7: Inserção de um tipo composto
// === Order Documentation(Includes all special types)===
db.orders.insertOne({
_id: ObjectId(),
orderNumber: "ORD-20260701-0001",
// String + Value
userId: "user_001",
total: NumberDecimal("1299.99"),
tax: NumberDecimal("130.00"),
// Array + Nested
items: [
{ sku: "LAPTOP-001", qty: 1, price: NumberDecimal("1299.99") },
{ sku: "MOUSE-001", qty: 2, price: NumberDecimal("29.99") }
],
// Status
status: "pending",
isPaid: false,
// Date
createdAt: new Date(),
expectedDelivery: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days
// Binary (PDF receipt)
receiptPdf: BinData(0, "JVBERi0xLjQKJ..."),
// Quote
shippingAddressId: ObjectId("507f1f77bcf86cd799439011"),
// Metadata
metadata: {
userAgent: "Mozilla/5.0...",
ipAddress: "192.168.1.1"
}
});
9. Solução de problemas comuns relacionados à inserção
Explicação do conceito: As operações de inserção podem falhar por diversos motivos — conflito _id (E11000), falha na validação do documento (121), documento BSON muito grande (16755) ou nome de campo inválido (2). Compreender os códigos de erro e as estratégias de tratamento é essencial para garantir uma operação estável em um ambiente de produção. O princípio básico da resolução de problemas é: primeiro, verifique o código de erro → identifique a causa do erro → selecione uma estratégia de tratamento.
Como funciona: O MongoDB realiza uma validação em várias camadas antes de gravar um documento: validação do formato BSON → validação do nome do campo (não são permitidos nomes que comecem com $, nem .) → validação do índice único _id → validação do esquema → validação do tamanho do documento (16 MB) → validação da profundidade de aninhamento (100 níveis). Uma falha em qualquer nível impedirá a gravação e retornará o código de erro correspondente.
Dicas de depuração:
- Ativar registro detalhado:
db.adminCommand({ setParameter: 1, logComponentVerbosity: { write: { verbosity: 2 } } }) - Visualizar o log de consultas lentas:
db.system.profile.find().sort({ ts: -1 }).limit(5) - Verificar o tamanho do documento:
BSON.calculateObjectSize(doc)Retorna o número de bytes - Verificar a profundidade de aninhamento: Função personalizada
getDepth()
Monitoramento do ambiente de produção:
- Latência de gravação:
db.serverStatus().opLatencies.writesMonitorar as tendências de latência de gravação - Taxa de transferência de gravação:
db.serverStatus().opcounters.insertMonitora o número de inserções por segundo - Operação atual:
db.currentOp({ "op": "insert" })Visualizar a operação de inserção atualmente em andamento
graph TB
A[insertOne Request] --> B{BSON Format Validation}
B -->|Failure| B1[Error Code 2<br/>Invalid field name]
B -->|Through| C{_id Single-Check Verification}
C -->|Conflict| C1[Error Code 11000<br/>Duplicate Keys]
C -->|Through| D{Schema Verification}
D -->|Failure| D1[Error Code 121<br/>Verification Failed]
D -->|Through| E{Document Size Verification}
E -->|More than16MB| E1[Error Code 16755<br/>The document is too large]
E -->|Through| F[Write successful ✅]
style F fill:#d4edda
style C1 fill:#f8d7da
style D1 fill:#f8d7da
| Código de erro | Significado | Causa principal | Estratégia de resolução |
|---|---|---|---|
| 11000 | _id é uma duplicata | Já existe um documento com o mesmo _id | Use upsert ou ordered: false |
| 121 | Falha na validação do documento | O valor do campo não está em conformidade com as regras do esquema | Verifique as regras de validação do esquema |
| 2 | Erro no nome do campo | O nome do campo começa com $ ou contém . |
Renomeie o campo |
| 16755 | Documento BSON muito grande | O documento excede o limite de 16 MB | Divida o documento ou use o GridFS |
| 14 | Tempo limite de Write Concern | Tempo limite de resposta do nó réplica | Aumente o wtimeout ou simplifique o w |
| 50 | Excede a profundidade máxima do BSON | Mais de 100 níveis de aninhamento | Reduza o número de níveis de aninhamento |
(1) Tabela de referência de códigos de erro
| Código de erro | Significado | Solução |
|---|---|---|
| 11000 | _id duplicado | Use upsert ou ordered: false |
| 121 | Falha na validação do documento | Verifique as regras de validação do esquema |
| 2 | Nome de campo inválido (por exemplo, que comece com $) |
Renomeie o campo |
| 16755 | O documento BSON é muito grande (>16 MB) | Divida o documento ou use o GridFS |
| 14 | Tempo limite de Write Concern | Aumente o wtimeout ou simplifique o w |
| 50 | Excederam a profundidade máxima do BSON | Reduzir os níveis de aninhamento |
(2) Dicas de depuração
// === Enable detailed logging ===
db.adminCommand({ setParameter: 1, logComponentVerbosity: { write: { verbosity: 2 } } });
// === View the slow query log ===
db.system.profile.find().sort({ ts: -1 }).limit(5);
// === Check the document size ===
const doc = { /* your document */ };
print(`Document Size:${BSON.calculateObjectSize(doc)} bytes`);
print(`Nesting Depth:${getDepth(doc)}`);
(3) Monitoramento de desempenho
// === View Current Database Operations ===
db.currentOp({ "op": "insert" });
// === Monitoring Write Performance ===
db.serverStatus().opcounters;
// {
// insert: 12345,
// query: 67890,
// update: 2345,
// delete: 100,
// ...
// }
// === View Write Latency ===
db.serverStatus().opLatencies.writes;
// { latency: 12345, ops: 10000 }
❓ Perguntas Frequentes
P: Qual é a diferença de desempenho entre
insertOneeinsertMany? R:insertManyé de 10 a 100 vezes mais rápido do que várias chamadas deinsertOneporque: (1) reduz as idas e voltas na rede; (2) o MongoDB as processa em lotes no lado do servidor; (3) reduz o número de atualizações de índice. Recomendamos um tamanho de lote de 500 a 5.000 registros.
P: Preciso gerar o _id manualmente? R: Não, não é necessário. Se você não o especificar, o MongoDB gera automaticamente um ObjectId (carimbo de data/hora + valor aleatório + contador) que é globalmente único. Especificar manualmente o _id é adequado para cenários que exigem uma chave primária de negócios (como um número de pedido).
P: Por que o
ordered: falseoferece melhor desempenho? R: Noordered: true, o MongoDB insere os dados sequencialmente e interrompe o processo se for detectado um erro; noordered: false, ele insere os dados em paralelo e simplesmente ignora a inserção com falha se for detectado um erro, resultando em melhor desempenho.ordered: falseé recomendado para ambientes de produção.
P: A preocupação com a “maioria” nas gravações sempre resulta em perda de dados? R: Não dentro do conjunto de réplicas. Após uma gravação no primário, a maioria dos secundários deve confirmar a gravação antes que uma resposta seja retornada. No entanto, se um nó ficar fora do ar, as gravações podem ficar lentas ou atingir o tempo limite. Isso pode ser resolvido configurando um
wtimeoutrazoável (por exemplo, 5 segundos).
P: Qual é o tamanho adequado de um lote para inserção em massa? R: Recomendamos 500 a 5.000 registros por lote, ou ajustar de acordo com o tamanho dos dados (cada lote < 16 MB). Lotes muito grandes podem resultar em tempos de solicitação excessivamente longos, enquanto lotes muito pequenos podem aumentar a sobrecarga da rede.
P: Como faço para ignorar o campo _id ao inserir dados? R: Remova o campo _id no nível do aplicativo (por exemplo,
const { _id, ...rest } = doc) e deixe que o MongoDB o gere automaticamente. Como alternativa, limpe o campo _id existente antes da importação.
P: Onde estão os gargalos de desempenho durante a inserção? R: Gargalos comuns: (1) Validação do índice único; (2) Espera por Write Concern (w: maioria); (3) Sincronização do conjunto de réplicas; (4) E/S de disco. Método de otimização: primeiro, remova todos os índices (exceto o índice único) e, em seguida, reconstrua-os após a importação.
📖 Resumo
- insertOne: Inserir um único documento e retorna acknowledged e insertedId
- insertMany: Inserção em massa; recomenda-se definir
ordered: falsepara ignorar as linhas com falha - O Write Concern controla o nível de confirmação de gravação: w: 0 / 1 / maioria + j: true
- Existem 5 estratégias para lidar com conflitos de _id: pular / sobrescrever / upsert / ignorar _id / tentar novamente
- Tamanho recomendado do lote: 500 a 5.000 registros por lote; isso pode melhorar o desempenho em 10 a 100 vezes
- Otimização de desempenho: bulkWrite + dropIndexes (durante a importação) + seleção de Write Concern
- Códigos de erro comuns: 11000 (duplicado), 121 (falha na validação), 16755 (tamanho excessivo)
📝 Exercícios
-
Exercício básico (⭐): Use
insertOnepara inserir três documentos de produtos de tipos diferentes (incluindoDecimal128,Date,ArrayeObject) e verifique oinsertedIdretornado. -
Questão básica (⭐): Use
insertManypara inserir 10 documentos de usuário de uma só vez, criando intencionalmente valores duplicados de_id, e compare as diferenças nos resultados entreordered: trueeordered: false. -
Exercício avançado (⭐⭐): Escreva um script para inserir em lote 1.000 documentos de produtos (com SKUs, títulos e preços gerados aleatoriamente), utilizando
ordered: falseeWrite Concern w: majority, e registre o tempo de inserção. -
Problema avançado (⭐⭐): Use
bulkWritepara implementar a lógica “atualizar se_idjá existir; caso contrário, inserir” (modo upsert), processando 100 registros mistos. -
Exercício avançado (⭐⭐): Escreva um script de teste de desempenho para comparar o tempo gasto em inserções de uma única linha (1.000 chamadas
insertOne) e inserções em lote (10 chamadasinsertMany, 100 linhas por lote) e analise as razões para as diferenças de desempenho. -
Desafio (⭐⭐⭐): Escreva uma ferramenta completa de migração de dados que leia 1 milhão de registros de pedidos (incluindo campos Decimal e DateTime) do MySQL, os converta para o formato BSON do MongoDB e os importe em massa. A ferramenta deve oferecer suporte a: (a) sincronização incremental; (b) novas tentativas em caso de falha; (c) exibição do andamento; (d) monitoramento de desempenho.