MongoDB: Otimização e monitoramento de desempenho
Última atualização: 2026-08-26
A otimização e o monitoramento do desempenho são a “última etapa” de um ambiente de produção — dominá-los pode evitar 90% dos incidentes de desempenho.
1. O que você vai aprender
- Ajuste do pool de conexões
- Otimização de consultas (hint / projeção / lean)
- Análise do registro de consultas lentas
- Ferramentas de monitoramento APM
- Lista de verificação para implantação em produção
graph LR
App[Node.js Applications] -->|Request| Pool[Connection Pool<br/>maxPoolSize=50]
Pool -->|Connect 1| M1[mongod<br/>Primary]
Pool -->|Connect 2| M2[mongod<br/>Secondary]
Pool -->|Connect 3| M3[mongod<br/>Secondary]
M1 -->|Copy| M2
M1 -->|Copy| M3
App -.->|explain| M1
App -.->|indexStats| M1
style Pool fill:#d4edda
style M1 fill:#cce5ff
2. Ajuste do pool de conexões
Por que o pool de conexões é importante? Toda vez que o MongoDB estabelece uma conexão TCP, são necessários: um handshake de três vias + autenticação SCRAM (3 idas e voltas) + inicialização da sessão — o que leva aproximadamente 10–50 ms. Em cenários de alta simultaneidade, se não houver um pool de conexões, a criação de uma nova conexão para cada solicitação pode levar a uma tempestade de conexões que sobrecarrega o banco de dados. O pool de conexões reduz o tempo de estabelecimento da conexão para menos de 1 ms por meio do pré-aquecimento e da reutilização das conexões.
Como funcionam os pools de conexão:
graph LR
subgraph "Node.js Applications"
R1[Request1] -->|Loan Out| Pool[(Connection Pool<br/>min=5 max=50)]
R2[Request2] -->|Loan Out| Pool
R3[Request3] -->|Wait in line| Queue[Waiting Queue<br/>waitQueueTimeoutMS]
end
Pool -->|Active Connections| Active[mongod Primary]
Pool -->|Idle Connections| Idle[Free Space Recovery<br/>maxIdleTimeMS]
R1 -.->|Return| Pool
R3 -.->|Get the Return Link| Pool
style Pool fill:#d4edda
style Queue fill:#fff3cd
style Active fill:#cce5ff
Guia para ajustar os parâmetros do pool de conexões:
| Parâmetro | Valor padrão | Estratégia de ajuste | Risco de definir um valor muito alto | Risco de definir um valor muito baixo |
|---|---|---|---|---|
| maxPoolSize | 100 | Concorrência máxima × 1,5 | Limite de conexões com o banco de dados atingido | Tempo limite da fila de solicitações |
| minPoolSize | 0 | Definido como 10% do pico | Inicialização lenta, alto consumo de memória | Atraso na inicialização a frio |
| maxIdleTimeMS | 0 | 30000 (30 segundos) | Criação frequente de novas conexões | Vazamentos de conexão |
| waitQueueTimeoutMS | 0 | 10000 (10 segundos) | Pilha de solicitações | Solicitações em espera permanente |
| serverSelectionTimeoutMS | 30000 | 5000 | Atraso prolongado | Falha rápida |
Fórmula para calcular o número de conexões: maxPoolSize = peak concurrency * average query time (seconds) * safety factor (1.5). Por exemplo, se houver 100 conexões simultâneas e o tempo médio de consulta for de 0,1 segundo, então maxPoolSize = 100 * 0,1 * 1,5 = 15.
// === mongoose Connection Pool Configuration ===
mongoose.connect(uri, {
maxPoolSize: 50, // Maximum Number of Connections(Default 100)
minPoolSize: 5, // Minimum Number of Connections(Default 0)
maxIdleTimeMS: 30000, // Connection Idle Timeout(Default: Unlimited)
waitQueueTimeoutMS: 10000 // Connection timeout
});
// === Monitor Connection Pool ===
const db = mongoose.connection.db;
const stats = await db.admin().command({ serverStatus: 1 });
console.log('Connections:', stats.connections);
| maxPoolSize | Aplicável |
|---|---|
| 10 | Pequenos aplicativos / Desenvolvimento |
| 50 | Aplicativos de médio porte / Serviços da Web |
| 100 | Aplicativos de grande porte / Alto tráfego |
| Mais de 200 | Modo de cluster / Origem da CDN |
3. Otimização de consultas
Metodologia de otimização de consultas: O objetivo principal da otimização de consultas é “reduzir a carga de trabalho do banco de dados” — por meio da varredura de menos documentos, da transferência de menos campos e da omissão de serializações desnecessárias. Etapas de otimização: ① Use explain() para diagnosticar o problema; ② Verifique se o índice foi acionado; ③ Reduza o número de campos retornados; ④ Reduza o número de registros retornados; ⑤ Omita a sobrecarga desnecessária do Mongoose.
Quatro técnicas essenciais para a otimização de consultas:
| Técnica de otimização | Princípio | Efeito | Código |
|---|---|---|---|
| a função hint() força o uso do índice | contorna a escolha incorreta do otimizador de consultas | COLLSCAN → IXSCAN | .hint({field: 1}) |
| projeção select() | Retorna apenas os campos necessários | Redução de mais de 90% na transferência de dados | .select('sku title price') |
| lean() pular hidratação | Não criar documento Mongoose | Aumento de desempenho de 5x | .lean() |
| limit() Limita o número de registros | Evita que sejam retornados muitos documentos | Reduz o uso de memória | .limit(20) |
Padrões comuns de consultas lentas e soluções:
| Modo de consultas lentas | Causa | Solução |
|---|---|---|
| COLLSCAN (Varredura completa da tabela) | Índice ausente ou falha no índice | Criar um índice apropriado + hint() |
| Muitos campos retornados | find() Não select |
Adicionar projeção .select() |
| $where + execução de JS | Executar funções JS por documento | Alternar para operadores nativos |
| $regex: sem âncora | Não é possível usar o índice | Adicione o prefixo ^ para definir uma âncora |
| Pular paginação detalhada | pular (lento com conjuntos de dados grandes) | Mudar para paginação por cursor |
| N+1 consultas | Consultar uma a uma em um loop | Mudar para $in ou $lookup |
(1) A função hint() força a indexação
// === Force Index Usage ===
const products = await Product.find({ category: 'Electronics' })
.hint({ category: 1 });
// === mongoose Equivalent ===
const products = await Product.find({ category: 'Electronics' })
.hint('category_1');
(2) projeção: Reduzir o número de campos
// === Query only the necessary fields ===
const products = await Product.find()
.select('sku title price') // Don't fetch description, images, etc.
.lean();
// === Reduce Network Traffic 90%+ ===
(3) O lean() ignora o hydrate
// === General Query: Build mongoose Document (slow) ===
const products = await Product.find();
// === lean(): Return plain object directly (fast) ===
const products = await Product.find().lean();
(4) Evite $where
// Slow: $where executes JavaScript
db.products.find({ $where: 'this.price > 1000' });
// Fast: Use $gt
db.products.find({ price: { $gt: 1000 } });
4. Análise de consultas lentas
Processo de análise de consultas lentas: Identifique as consultas lentas → Ative o Profiler → Analise explain() → Identifique os gargalos → Otimize → Verifique. Existem três níveis de perfilagem: 0 (desativado), 1 (registrar consultas lentas) e 2 (registrar todas as consultas). Use o Nível 1 em ambientes de produção; o Nível 2 acarreta uma sobrecarga de desempenho.
Fluxo de trabalho para diagnóstico de consultas lentas:
graph TD
Start[Identifying Slow Queries] --> Enable[Enable Profiling Level 1<br/>slowms=100]
Enable --> Collect[Collect Slow Query Logs<br/>system.profile]
Collect --> Explain[explain executionStats<br/>Analyze the Execution Plan]
Explain --> Stage{Stage Type?}
Stage -->|COLLSCAN| AddIdx[Add an Index]
Stage -->|IXSCAN| CheckProj[Check the projection/Number of Articles]
Stage -->|FETCH| OptProj[Optimization select]
AddIdx --> Verify[Verify the effectiveness of the optimization]
CheckProj --> Verify
OptProj --> Verify
Verify --> Done[Performance Meets Standards ✅]
style COLLSCAN fill:#f8d7da
style Done fill:#d4edda
A função explain() exibe os campos-chave:
| Campo | Descrição | Valor normal | Valor anormal |
|---|---|---|---|
| estágio | Método de digitalização | IXSCAN | COLLSCAN |
| totalKeysExamined | Chaves de índice verificadas | ~ nReturned | >> nReturned |
| totalDocsExamined | Documentos digitalizados | ~ nReturned | >> nReturned |
| nRetornados | Número de documentos retornados | — | — |
| executionTimeMillis | Tempo de execução | < 100 ms | > 1000 ms |
| índice utilizado | Índice utilizado | Nome do índice composto | Nenhum (COLLSCAN) |
Proporção Áurea: totalDocsExamined : nReturned ≈ 1:1. Se, após a varredura de 10.000 registros, forem retornados apenas 20, isso significa que o índice não é preciso o suficiente.
// === Enable the slow query log ===
mongoose.connection.db.admin().command({
setParameter: 1,
slowms: 100 // Record > 100ms Queries
});
// === mongoose Enable in the middle debug ===
mongoose.set('debug', true);
// Output all queries to console
// === mongoose-debug Slow Query Log ===
mongoose.set('debug', (collectionName, method, query, doc) => {
const start = Date.now();
console.log(`${collectionName}.${method}(${JSON.stringify(query)})`);
});
5. Otimização de índices
Princípios fundamentais da otimização de índices: Ter mais índices não significa necessariamente que seja melhor — cada índice ocupa espaço em disco, aumenta a sobrecarga de gravação (já que os índices precisam ser atualizados a cada inserção, atualização ou exclusão) e consome memória (o MongoDB tenta manter os índices na RAM). O segredo para otimizar os índices é criar índices úteis, remover os desnecessários e escolher o tipo de índice correto.
Métricas de verificação de integridade do índice:
| Parâmetro | Como obter | Valor normal | Valor anormal |
|---|---|---|---|
| Uso do índice | $indexStats | accesses.ops > 0 | ops = 0 (não utilizado) |
| Tamanho do índice | db.stats() | < 50% da RAM | > RAM (troca frequente) |
| Número de índices | getIndexes() | < 10 / coleção | > 20 (demais) |
| Latência de gravação | status do servidor | < 10 ms | > 100 ms (o índice está retardando as gravações) |
Decisões sobre otimização de índices:
| Cenário | Ação | Motivo |
|---|---|---|
| Índice com ops=0 | Excluir | Puro desperdício de espaço e desempenho de gravação |
| Índices redundantes | Manter índices compostos, remover índices de campo único | {a:1, b:1} já abrange {a:1} |
| Índice de base baixa | Excluir ou modificar alguns índices | isActive tem apenas dois valores, portanto, apresenta baixa seletividade |
| Verificar se há índices não utilizados | Adicionar ou usar hint() | Desastre de desempenho com COLLSCAN |
// === Index Usage Analysis ===
db.products.aggregate([{ $indexStats: {} }]);
// Find unused indexes
// === Index Usage in Slow Queries ===
db.system.profile.find({ millis: { $gt: 100 } })
.sort({ ts: -1 })
.limit(20)
.forEach(profile => {
console.log('Query:', profile.command.find);
console.log('Plan:', profile.planSummary);
console.log('Time:', profile.millis, 'ms');
});
6. Ferramentas de monitoramento APM
Por que você precisa do APM? Os problemas em ambientes de produção não vêm com aviso prévio — picos repentinos de conexões, aumento no número de consultas lentas e vazamentos de memória ocorrem gradualmente. O APM (Monitoramento de Desempenho de Aplicações) oferece coleta de métricas em tempo real, painéis visuais e alertas de limite, permitindo que você identifique e resolva problemas antes que os usuários reclamem.
Pirâmide de métricas de monitoramento:
graph TB
L4[Business Metrics<br/>Orders/Error Rate/Response Time] --> L3[Application Metrics<br/>QPS/Latency/Node.js Memory]
L3 --> L2[Database Metrics<br/>Number of connections/Slow Queries/Index Hit Rate]
L2 --> L1[Infrastructure<br/>CPU/Memory/Disk/Internet]
style L4 fill:#d4edda
style L1 fill:#fff3cd
Indicadores-chave de desempenho:
| Categoria da métrica | Métrica específica | Limite de alerta | Método de coleta |
|---|---|---|---|
| Conexões | Conexões atuais | > maxPoolSize × 80% | serverStatus.connections |
| Consulta | Número de consultas lentas | > 10/min | system.profile |
| Consulta | Tempo médio de consulta | > 200 ms | Mongoose Debug / APM |
| Índice | Taxa de acertos do índice | < 95% | $indexStats |
| Memória | Memória residente | > 80% da RAM disponível | serverStatus.mem |
| Disco | Uso do disco | > 80% | db.stats() |
| Instância | Atraso na replicação | > 10 s | rs.status().lag |
| Ferramenta | Recursos | Aplicações |
|---|---|---|
| Monitoramento do MongoDB Atlas | Oficial, integrado | Usuários do Atlas |
| Prometheus + mongo_exporter | Código aberto, auto-hospedado | Produção em grande escala |
| Datadog APM | Comercial, Full-Stack | Empresarial |
| New Relic | Negócios, Full-Stack | Corporativo |
| Prometheus + Grafana | Código aberto, gratuito | Monitoramento auto-hospedado |
7. Lista de verificação para implantação em produção
Considerações importantes para ambientes de produção: A implantação em produção não se resume apenas a “garantir que o código funcione”; trata-se de assegurar alta disponibilidade (um único ponto de falha não afeta o serviço), segurança dos dados (sem perda ou vazamento de dados), observabilidade (a capacidade de identificar rapidamente os problemas) e capacidade de recuperação (a capacidade de restaurar a partir de backups).
Os Quatro Pilares da Implantação em Produção:
| Pilar | Objetivo | Principais medidas |
|---|---|---|
| Alta disponibilidade | Disponibilidade de 99,9% ou mais | Conjunto de réplicas de 3 nós + failover automático |
| Segurança de dados | Sem perda de dados + Sem vazamentos de dados | Nível de garantia de gravação: maioria + TLS + RBAC |
| Observabilidade | Identifique problemas em até 5 minutos | Logs + Métricas + Alertas + APM |
| Capacidade de recuperação | RPO < 1 h, RTO < 4 h | Backups programados + PITR + Simulados de recuperação |
## 10. Performance Optimization Checklist
---
### (1) Database
- [ ] Dungeon Collection 3 Node(High Availability)
- [ ] Write Concern majority(No data loss)
- [ ] Read Preference primaryPreferred
- [ ] Slow Query Monitoring Enabled
- [ ] Index Usage Monitoring
- [ ] Connection Pool maxPoolSize Settings
### (2) Application Layer
- [ ] mongoose lean() For read-only queries
- [ ] projection Search only the required fields
- [ ] bulkWrite Bulk Operations
- [ ] Avoid $where / $regex Unanchored
- [ ] Error-handling middleware
- [ ] Health Check Endpoints /healthz
### (3) Operations and Maintenance
- [ ] Daily mongodump Backup
- [ ] Backup Retention 7-30 days
- [ ] Monitoring Alerts(Number of connections / Slow Queries / Disk)
- [ ] TLS/SSL Encrypted Transmission
- [ ] SCRAM + RBAC Access Control
- [ ] Atlas PITR or oplog Continuous Backup
8. Prática: Ajuste abrangente de desempenho
Metodologia prática para ajuste de desempenho: O ajuste de desempenho não consiste em fazer otimizações baseadas em suposições; trata-se de um processo em ciclo fechado de “medir → analisar → otimizar → verificar”. Primeiro, use o explain() para medir o desempenho atual e identificar gargalos (varreduras completas de tabelas? Transferência excessiva de dados? Sobrecarga do Hydrate?), depois implemente otimizações direcionadas e, por fim, use o explain() novamente para verificar os resultados.
Ajuste de desempenho em circuito fechado:
graph LR
A[Measurement: explain + Slow Log] --> B[Analysis: Bottleneck Identification]
B --> C[Optimization: Index/Projection/lean]
C --> D[Verify: use explain again]
D -->|Did not meet the standard| A
D -->|Meet the requirements| E[Launch ✅]
style A fill:#cce5ff
style C fill:#d4edda
style E fill:#d4edda
Estudo de caso de otimização do ShopHub: Alice, da TechCorp, otimizou a API da lista de produtos, reduzindo o tempo de resposta de 3 segundos para 50 ms — ① explain() revelou um COLLSCAN → adicionou um índice composto {category:1, isActive:1, createdAt:-1}; ② Retornava todos os campos → utilizou select() para consultar apenas 5; ③ Retornava um documento Mongoose → adicionou lean(); ④ find e count eram sequenciais → utilizou Promise.all para processamento paralelo.
// === Slow Queries Before Optimization ===
app.get('/api/products', async (req, res) => {
const products = await Product.find({ isActive: true });
// 100 Full-Table Scan of 10,000 Documents, ~3 seconds
res.json(products);
});
// === After optimization ===
app.get('/api/products', async (req, res) => {
const { page = 1, limit = 20, category, search } = req.query;
// 1. Building Indexes to Optimize Queries
const query = { isActive: true };
if (category) query.category = category;
if (search) query.title = new RegExp(search, 'i');
// 2. Projection + lean + limit
const products = await Product.find(query)
.select('sku title price thumbnail') // Projection
.hint({ isActive: 1, category: 1 }) // Forced Index
.limit(Math.min(+limit, 100)) // Maximum Limit
.skip((+page - 1) * +limit)
.lean(); // Performance Optimization
// 3. Parallel count
const total = await Product.countDocuments(query);
res.json({ data: products, meta: { page: +page, limit: +limit, total } });
});
// After optimization:~50ms(Performance ↑60x)
▶ Exemplo 1: Diagnósticos do explain() + Otimização de índices
// === Scenario: ShopHub product list queries are slow, Alice uses explain to diagnose ===
// 1. Diagnose the Current Query
const explain = await Product.find({ category: 'Electronics', isActive: true })
.sort({ createdAt: -1 })
.limit(20)
.explain('executionStats');
console.log('Stage:', explain.queryPlanner.winningPlan.stage);
// Output:COLLSCAN ❌ Full Table Scan!
console.log('Docs examined:', explain.executionStats.totalDocsExamined);
// Output:1000000(Scanned all of them 100 10,000 Documents)
console.log('Docs returned:', explain.executionStats.nReturned);
// Output: 20 (Returned only 20 docs)
console.log('Time:', explain.executionStats.executionTimeMillis, 'ms');
// Output: 3200ms (too slow!)
// 2. Create a composite index
db.products.createIndex({ category: 1, isActive: 1, createdAt: -1 });
// 3. Once again explain Verification
const explain2 = await Product.find({ category: 'Electronics', isActive: true })
.sort({ createdAt: -1 })
.limit(20)
.hint({ category: 1, isActive: 1, createdAt: -1 })
.explain('executionStats');
console.log('Stage:', explain2.queryPlanner.winningPlan.stage);
// Output:IXSCAN ✅ Using Indexes!
console.log('Docs examined:', explain2.executionStats.totalDocsExamined);
// Output:20(Precise Scanning)
console.log('Time:', explain2.executionStats.executionTimeMillis, 'ms');
// Output:5ms(↑640x Performance Improvements!)
Resultado: O diagnóstico explain() identificou um COLLSCAN → criou um índice composto → IXSCAN, e o tempo de execução da consulta caiu de 3.200 ms para 5 ms.
▶ Exemplo: Query Performance Diagnosis com explain() (Difficulty ⭐⭐)
// Scene: ShopHub slow product search diagnosis - is the index being used?
const mongoose = require('mongoose');
const Product = mongoose.model('Product');
async function diagnoseQuery() {
// Step 1: Run query with explain
const explanation = await Product.find({
category: 'Electronics',
price: { $gte: 100, $lte: 1000 }
})
.sort({ price: -1 })
.explain('executionStats');
const stats = explanation.executionStats;
console.log('=== Query Performance Report ===');
console.log(`Execution Time: ${stats.executionTimeMillis} ms`);
console.log(`Documents Examined: ${stats.totalDocsExamined}`);
console.log(`Documents Returned: ${stats.nReturned}`);
console.log(`Index Used: ${stats.winningPlan.inputStage?.indexName || 'COLLSCAN'}`);
// Step 2: Check for problems
const problems = [];
if (stats.executionTimeMillis > 100) {
problems.push('Slow query (>100ms)');
}
if (stats.totalDocsExamined > stats.nReturned * 10) {
problems.push(`High scan ratio: ${stats.totalDocsExamined}/${stats.nReturned}`);
}
if (!stats.winningPlan.inputStage?.indexName) {
problems.push('No index used (COLLSCAN)');
}
// Step 3: Suggest fix
if (problems.length > 0) {
console.log('\n⚠️ Problems detected:');
problems.forEach(p => console.log(` - ${p}`));
console.log('\n💡 Suggested index:');
console.log(' ProductSchema.index({ category: 1, price: -1 })');
} else {
console.log('\n✅ Query performance is good');
}
}
diagnoseQuery();
Saída:
TEXT 📖 Somente leitura=== Query Performance Report === Execution Time: 245 ms Documents Examined: 50000 Documents Returned: 120 Index Used: undefined ⚠️ Problems detected: - Slow query (>100ms) - High scan ratio: 50000/120 - No index used (COLLSCAN) 💡 Suggested index: ProductSchema.index({ category: 1, price: -1 })
▶ Exemplo 2: Guia prático para otimização abrangente de desempenho (pool de conexões + indexação + otimização + monitoramento)
// === Scene:Product List API Performance Optimization(3s → 50ms)===
// === Before Optimization (slow) ===
app.get('/api/products', async (req, res) => {
const products = await Product.find(); // Full Table Scan + Return all fields
res.json(products);
});
// 100 10,000 Documents,~3000ms,~50MB Data
// === After optimization (fast) ===
// 1. Enable mongoose debug(Monitoring Queries During Development)
mongoose.set('debug', (coll, method, query) => {
console.log(`${coll}.${method}(${JSON.stringify(query)})`);
});
// 2. Optimizing the Connection Pool
mongoose.connect(uri, {
maxPoolSize: 50, // Adjust Based on Concurrency
minPoolSize: 5,
maxIdleTimeMS: 30000,
waitQueueTimeoutMS: 10000
});
// 3. Enable the slow query log(>100ms)
mongoose.connection.db.admin().command({
setParameter: 1,
slowms: 100
});
// 4. Create Appropriate Indexes
db.products.createIndex({ category: 1, isActive: 1, createdAt: -1 });
db.products.createIndex({ sku: 1 }, { unique: true });
db.products.createIndex({ title: 'text', description: 'text' });
// 5. Optimize Queries:projection + lean + hint + limit
app.get('/api/products', async (req, res) => {
const { page = 1, limit = 20, category, search } = req.query;
const query = { isActive: true };
if (category) query.category = category;
if (search) query.title = new RegExp(search, 'i');
const [products, total] = await Promise.all([
Product.find(query)
.select('sku title price thumbnail rating') // Projection:Return only 5 field
.hint({ category: 1, isActive: 1, createdAt: -1 }) // Forced Index
.sort({ createdAt: -1 })
.skip((page - 1) * limit)
.limit(Math.min(+limit, 100))
.lean(), // Skip mongoose hydrate
Product.countDocuments(query)
]);
res.json({
success: true,
data: products,
meta: { page: +page, limit: +limit, total, pages: Math.ceil(total / limit) }
});
});
// 100 10,000 Documents,~50ms(Performance ↑60x),~200KB Data(Reduce 99.6%)
// === 6. explain() Verify that the index is active ===
const explain = await Product.find({ category: 'Electronics', isActive: true })
.sort({ createdAt: -1 })
.limit(20)
.explain('executionStats');
console.log('Stage:', explain.queryPlanner.winningPlan.stage);
// Output:IXSCAN(An index was used)
console.log('Docs examined:', explain.executionStats.totalDocsExamined);
console.log('Keys examined:', explain.executionStats.totalKeysExamined);
console.log('Returned:', explain.executionStats.nReturned);
console.log('Time:', explain.executionStats.executionTimeMillis, 'ms');
// === 7. Monitoring Alerts ===
// 7.1 Monitor Connection Count
const connStatus = await mongoose.connection.db.admin().command({ serverStatus: 1 });
if (connStatus.connections.current > 1000) {
console.warn(`⚠️ Too many connections: ${connStatus.connections.current}`);
// Send an Alert (Email/DingTalk/Slack)
}
// 7.2 Monitoring Slow Queries
const slowQueries = await mongoose.connection.db.collection('system.profile')
.find({ millis: { $gt: 100 } })
.sort({ ts: -1 })
.limit(10)
.toArray();
slowQueries.forEach(q => {
console.log(`[${q.ts}] ${q.command.find}: ${q.millis}ms`);
console.log(` Plan: ${q.planSummary}`);
});
// 7.3 Index Usage Rate
const indexStats = await mongoose.connection.db.collection('products')
.aggregate([{ $indexStats: {} }])
.toArray();
const unused = indexStats.filter(s => s.accesses.ops === 0);
unused.forEach(i => {
console.log(`⚠️ Index not used: ${i.name}`);
// Automatic Deletion(Caution in Production Environments)
// await mongoose.connection.db.collection('products').dropIndex(i.name);
});
// === 8. APM Integration(Datadog/New Relic)===
const tracer = require('dd-trace').init();
tracer.use('mongoose', { service: 'shopdb' });
// All mongoose Query Auto-Tracking,Performance Data Reporting Datadog
Resultados: Por meio de uma otimização abrangente do pool de conexões, dos índices, do Lean e das projeções, o desempenho das consultas melhorou de 3 segundos para 50 milissegundos (um aumento de 60 vezes), e a transferência de dados foi reduzida em 99,6%.
❓ Perguntas Frequentes
P: Um valor maior para maxPoolSize é sempre melhor? R: Não. Definir um valor muito alto esgotará as conexões do banco de dados (o valor padrão de maxIncomingConnections do MongoDB é 65536).
P: Quais são os efeitos colaterais do
lean()? R: Você perde o acesso aos métodos de documento do Mongoose (comosaveepopulate). Ele foi projetado para ser usado com a API exclusiva para consultas.
P: Como posso saber se uma consulta está usando um índice? R:
query.explain('executionStats')Verifique a fase: IXSCAN (varredura por índice) / COLLSCAN (varredura completa da tabela).
📖 Resumo
- Ajuste do pool de conexões: maxPoolSize / minPoolSize
- Otimização de consultas: hint / projeção / lean / evitar $where
- Análise de consultas lentas: setProfilingLevel / mongoose debug
- Monitoramento do uso de índices: $indexStats / system.profile
- Ferramentas de APM: Atlas / Prometheus / Datadog
- Lista de verificação de produção: Conjuntos de réplicas + Backups + Monitoramento + Segurança
📝 Exercícios
- Problema básico (⭐): Use
lean()para otimizar a API da lista de produtos e comparar as diferenças de desempenho. - Questão básica (⭐): Ative o log de consultas lentas e analise as 10 consultas mais lentas.
- Problema avançado (⭐⭐): Use uma dica para forçar a indexação e compare o desempenho do COLLSCAN e do IXSCAN.
- Problema avançado (⭐⭐): Analise $indexStats para identificar índices não utilizados e excluí-los.
- Desafio (⭐⭐⭐): Realize um ajuste abrangente de desempenho (pool de conexões + índices + otimização + monitoramento) e compare o desempenho antes e depois.