MongoDB: 性能优化与监控:生产级调优

最后更新:2026-08-26

性能优化与监控是生产环境的最后一公里——掌握它能避免 90% 的性能事故。

1. 你将学到



100%
graph LR
    App[Node.js 应用] -->|请求| Pool[Connection Pool<br/>maxPoolSize=50]

    Pool -->|连接 1| M1[mongod<br/>Primary]
    Pool -->|连接 2| M2[mongod<br/>Secondary]
    Pool -->|连接 3| M3[mongod<br/>Secondary]

    M1 -->|复制| M2
    M1 -->|复制| M3

    App -.->|explain| M1
    App -.->|indexStats| M1

    style Pool fill:#d4edda
    style M1 fill:#cce5ff

2. 连接池调优

连接池为什么重要? MongoDB 每建立一个 TCP 连接需要:3 次握手 + SCRAM 认证(3 次往返)+ 会话初始化——约 10-50ms。在高并发场景下,如果没有连接池,每请求新建连接会导致连接风暴,拖垮数据库。连接池通过预热和复用连接,将连接获取时间降到 < 1ms。

连接池工作机制

100%
graph LR
    subgraph "Node.js 应用"
        R1[请求1] -->|借出| Pool[(连接池<br/>min=5 max=50)]
        R2[请求2] -->|借出| Pool
        R3[请求3] -->|排队等待| Queue[等待队列<br/>waitQueueTimeoutMS]
    end
    
    Pool -->|活跃连接| Active[mongod Primary]
    Pool -->|空闲连接| Idle[空闲回收<br/>maxIdleTimeMS]
    R1 -.->|归还| Pool
    R3 -.->|获取归还连接| Pool

    style Pool fill:#d4edda
    style Queue fill:#fff3cd
    style Active fill:#cce5ff

连接池参数调优指南

参数 默认值 调优策略 过大风险 过小风险
maxPoolSize 100 按并发峰值 × 1.5 耗尽数据库连接 请求排队超时
minPoolSize 0 设为峰值 × 10% 启动慢、占内存 冷启动延迟
maxIdleTimeMS 0 30000(30秒) 频繁新建连接 连接泄漏
waitQueueTimeoutMS 0 10000(10秒) 请求积压 请求永久等待
serverSelectionTimeoutMS 30000 5000 长时间卡住 快速失败

连接数计算公式maxPoolSize = 并发峰值 × 平均查询时间(秒) × 安全系数(1.5)。如并发 100,平均查询 0.1s,则 maxPoolSize = 100 × 0.1 × 1.5 = 15。

JAVASCRIPT
// === mongoose 连接池配置 ===
mongoose.connect(uri, {
  maxPoolSize: 50,          // 最大连接数(默认 100)
  minPoolSize: 5,           // 最小连接数(默认 0)
  maxIdleTimeMS: 30000,     // 连接空闲超时(默认无限)
  waitQueueTimeoutMS: 10000 // 等待连接超时
});

// === 监控连接池 ===
const db = mongoose.connection.db;
const stats = await db.admin().command({ serverStatus: 1 });
console.log('Connections:', stats.connections);
maxPoolSize 适用
10-20 小型应用(< 1000 DAU)
30-50 中型应用(1K-100K DAU)
50-100 大型应用(> 100K DAU)
100+ 超大流量(需谨慎,避免耗尽)

3. 查询优化

查询优化方法论:查询优化的核心目标是"减少数据库工作量"——扫描更少的文档、传输更少的字段、跳过不必要的序列化。优化步骤:① 用 explain() 诊断问题;② 确认索引是否命中;③ 减少返回字段;④ 减少返回条数;⑤ 跳过不必要的 mongoose 开销。

查询优化四板斧

优化手段 原理 效果 代码
hint() 强制索引 绕过查询优化器的错误选择 COLLSCAN → IXSCAN .hint({field: 1})
select() 投影 只返回必要字段 传输量 ↓90%+ .select('sku title price')
lean() 跳过 hydrate 不构造 mongoose Document 性能 ↑5x .lean()
limit() 限制条数 避免返回过多文档 内存 ↓ .limit(20)

常见慢查询模式与修复

慢查询模式 原因 修复方案
COLLSCAN(全表扫描) 缺索引或索引未命中 创建合适索引 + hint()
返回过多字段 find() 无 select 加 .select() 投影
$where + JS 执行 每文档执行 JS 函数 改用原生操作符
$regex 无锚定 无法用索引 加 ^ 前缀锚定
深分页 skip skip 大量数据很慢 改用 cursor 分页
N+1 查询 循环中逐个查询 改用 $in 或 $lookup

(1) hint() 强制索引

JAVASCRIPT
// === 强制使用索引 ===
const products = await Product.find({ category: 'Electronics' })
  .hint({ category: 1 });

// === mongoose 等价 ===
const products = await Product.find({ category: 'Electronics' })
  .hint('category_1');

(2) projection 减少字段

JAVASCRIPT
// === 只查询必要字段 ===
const products = await Product.find()
  .select('sku title price')  // 不查 description, images 等
  .lean();

// === 减少网络传输 90%+ ===

(3) lean() 跳过 hydrate

JAVASCRIPT
// === 普通查询:构造 mongoose Document(慢)===
const products = await Product.find();

// === lean():直接返回 plain object(快)===
const products = await Product.find().lean();

(4) 避免 $where

JAVASCRIPT
// ❌ 慢:$where 执行 JavaScript
db.products.find({ $where: 'this.price > 100' });

// ✅ 快:使用 $gt
db.products.find({ price: { $gt: 100 } });

4. 慢查询分析

慢查询分析流程:发现慢查询→启用 Profiler→分析 explain()→定位瓶颈→优化→验证。Profiling Level 分 3 级:0(关闭)、1(记录慢查询)、2(记录所有查询)。生产环境用 Level 1,Level 2 有性能开销。

慢查询诊断工作流

100%
graph TD
    Start[发现慢查询] --> Enable[启用 Profiling Level 1<br/>slowms=100]
    Enable --> Collect[收集慢查询日志<br/>system.profile]
    Collect --> Explain[explain executionStats<br/>分析执行计划]
    Explain --> Stage{Stage 类型?}
    Stage -->|COLLSCAN| AddIdx[添加索引]
    Stage -->|IXSCAN| CheckProj[检查投影/条数]
    Stage -->|FETCH| OptProj[优化 select]
    AddIdx --> Verify[验证优化效果]
    CheckProj --> Verify
    OptProj --> Verify
    Verify --> Done[性能达标 ✅]

    style COLLSCAN fill:#f8d7da
    style Done fill:#d4edda

explain() 输出关键字段

字段 含义 健康值 异常值
stage 扫描方式 IXSCAN COLLSCAN
totalKeysExamined 索引键扫描数 ≈ nReturned >> nReturned
totalDocsExamined 文档扫描数 ≈ nReturned >> nReturned
nReturned 返回文档数
executionTimeMillis 执行时间 < 100ms > 1000ms
indexUsed 使用的索引 复合索引名 无(COLLSCAN)

黄金比例totalDocsExamined : nReturned ≈ 1:1。如果扫描了 10000 条只返回 20 条,说明索引不够精确。

JAVASCRIPT
// === 启用慢查询日志 ===
mongoose.connection.db.admin().command({
  setParameter: 1,
  slowms: 100  // 记录 > 100ms 的查询
});

// === mongoose 中启用 debug ===
mongoose.set('debug', true);
// 输出所有查询到 console

// === mongoose-debug 慢查询日志 ===
mongoose.set('debug', (collectionName, method, query, doc) => {
  const start = Date.now();
  console.log(`${collectionName}.${method}(${JSON.stringify(query)})`);
});

5. 索引优化

索引优化核心原则:索引不是越多越好——每个索引占用磁盘空间、增加写入开销(每次 insert/update/delete 都要更新索引)、占用内存(MongoDB 尽量把索引放在 RAM)。优化索引的关键是:建有用的索引、删无用的索引、选对索引类型。

索引健康度检查指标

指标 获取方式 健康值 异常值
索引使用率 $indexStats accesses.ops > 0 ops = 0(未使用)
索引大小 db.stats() < RAM 的 50% > RAM(频繁换页)
索引数量 getIndexes() < 10 / 集合 > 20(过多)
写入延迟 serverStatus < 10ms > 100ms(索引拖慢写入)

索引优化决策

场景 操作 原因
ops=0 的索引 删除 纯浪费空间和写入性能
功能重复的索引 保留复合索引,删单字段 {a:1,b:1} 已覆盖 {a:1}
低基数索引 删除或改部分索引 isActive 只有2值,区分度低
查询未走索引 添加或 hint() COLLSCAN 性能灾难
JAVASCRIPT
// === 索引使用分析 ===
db.products.aggregate([{ $indexStats: {} }]);
// 找出 unused indexes

// === 慢查询中的索引使用 ===
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. APM 监控工具

为什么需要 APM? 生产环境的问题不会提前通知你——连接数飙升、慢查询增长、内存泄漏都是渐进发生的。APM(Application Performance Monitoring)提供实时指标采集、可视化仪表盘、阈值告警,让你在用户投诉之前发现并解决问题。

监控指标金字塔

100%
graph TB
    L4[业务指标<br/>订单量/错误率/响应时间] --> L3[应用指标<br/>QPS/延迟/Node.js 内存]
    L3 --> L2[数据库指标<br/>连接数/慢查询/索引命中率]
    L2 --> L1[基础设施<br/>CPU/内存/磁盘/网络]

    style L4 fill:#d4edda
    style L1 fill:#fff3cd

核心监控指标

指标类别 具体指标 告警阈值 采集方式
连接 当前连接数 > maxPoolSize × 80% serverStatus.connections
查询 慢查询数 > 10/min system.profile
查询 平均查询时间 > 200ms mongoose debug / APM
索引 索引命中率 < 95% $indexStats
内存 Resident Memory > 可用 RAM 80% serverStatus.mem
磁盘 磁盘使用率 > 80% db.stats()
副本 复制延迟 > 10s rs.status().lag
工具 特点 适用
MongoDB Atlas Monitoring 官方,自带 Atlas 用户
Prometheus + mongo_exporter 开源,自建 大型生产
Datadog APM 商业,全栈 企业
New Relic 商业,全栈 企业
Prometheus + Grafana 开源,免费 自建监控

7. 生产部署 Checklist

生产环境的核心关切:生产部署不是"代码能跑就行",而是要确保:高可用(单点故障不影响服务)、数据安全(不丢数据、不泄露)、可观测(出问题能快速定位)、可恢复(备份可还原)。

生产部署四大支柱

支柱 目标 关键措施
高可用 99.9%+ 可用性 副本集 3 节点 + 自动故障转移
数据安全 不丢数据 + 不泄露 Write Concern majority + TLS + RBAC
可观测性 5 分钟内定位问题 日志 + 指标 + 告警 + APM
可恢复性 RPO < 1h, RTO < 4h 定时备份 + PITR + 恢复演练
MARKDOWN
## 10. 性能优化 Checklist
---

### (1) 数据库
- [ ] 副本集 3 节点(高可用)
- [ ] Write Concern majority(数据不丢)
- [ ] Read Preference primaryPreferred
- [ ] 慢查询监控启用
- [ ] 索引使用率监控
- [ ] 连接池 maxPoolSize 设置

### (2) 应用层
- [ ] mongoose lean() 用于纯查询
- [ ] projection 只查必要字段
- [ ] bulkWrite 批量操作
- [ ] 避免 $where / $regex 无锚定
- [ ] 错误处理中间件
- [ ] 健康检查端点 /healthz

### (3) 运维
- [ ] 每日 mongodump 备份
- [ ] 备份保留 7-30 天
- [ ] 监控告警(连接数 / 慢查询 / 磁盘)
- [ ] TLS/SSL 加密传输
- [ ] SCRAM + RBAC 权限控制
- [ ] Atlas PITR 或 oplog 持续备份

8. 实战:综合性能调优

性能调优实战方法论:性能调优不是拍脑袋优化,而是"测量→分析→优化→验证"的闭环。先用 explain() 测量当前性能,定位瓶颈(全表扫描?传输过多?hydrate 开销?),针对性优化,再用 explain() 验证效果。

性能调优闭环

100%
graph LR
    A[测量: explain + 慢日志] --> B[分析: 瓶颈定位]
    B --> C[优化: 索引/投影/lean]
    C --> D[验证: 再 explain]
    D -->|未达标| A
    D -->|达标| E[上线 ✅]

    style A fill:#cce5ff
    style C fill:#d4edda
    style E fill:#d4edda

ShopHub 优化案例:TechCorp 的 Alice 优化商品列表 API,从 3s 降到 50ms——① explain() 发现 COLLSCAN → 加复合索引 {category:1, isActive:1, createdAt:-1};② 返回所有字段 → select() 只查 5 个;③ 返回 mongoose Document → 加 lean();④ find 和 count 串行 → Promise.all 并行。

JAVASCRIPT
// === 优化前的慢查询 ===
app.get('/api/products', async (req, res) => {
  const products = await Product.find({ isActive: true });
  // 100 万文档全表扫描,~3 秒
  res.json(products);
});

// === 优化后 ===
app.get('/api/products', async (req, res) => {
  const { page = 1, limit = 20, category, search } = req.query;

  // 1. 构建索引优化查询
  const query = { isActive: true };
  if (category) query.category = category;
  if (search) query.title = new RegExp(search, 'i');

  // 2. 投影 + lean + limit
  const products = await Product.find(query)
    .select('sku title price thumbnail')  // 投影
    .hint({ isActive: 1, category: 1 })  // 强制索引
    .limit(Math.min(+limit, 100))  // 限制最大
    .skip((+page - 1) * +limit)
    .lean();  // 性能优化

  // 3. 并行 count
  const total = await Product.countDocuments(query);

  res.json({ data: products, meta: { page: +page, limit: +limit, total } });
});
// 优化后:~50ms(性能 ↑60x)

▶ 示例 1:explain() 诊断 + 索引优化

JAVASCRIPT
// === 场景:ShopHub 商品列表查询慢,Alice 用 explain 诊断 ===

// 1. 诊断当前查询
const explain = await Product.find({ category: 'Electronics', isActive: true })
  .sort({ createdAt: -1 })
  .limit(20)
  .explain('executionStats');

console.log('Stage:', explain.queryPlanner.winningPlan.stage);
// 输出:COLLSCAN ❌ 全表扫描!

console.log('Docs examined:', explain.executionStats.totalDocsExamined);
// 输出:1000000(扫描了全部 100 万文档)

console.log('Docs returned:', explain.executionStats.nReturned);
// 输出:20(只返回 20 条)

console.log('Time:', explain.executionStats.executionTimeMillis, 'ms');
// 输出:3200ms(太慢!)

// 2. 创建复合索引
db.products.createIndex({ category: 1, isActive: 1, createdAt: -1 });

// 3. 再次 explain 验证
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);
// 输出:IXSCAN ✅ 使用索引!

console.log('Docs examined:', explain2.executionStats.totalDocsExamined);
// 输出:20(精确扫描)

console.log('Time:', explain2.executionStats.executionTimeMillis, 'ms');
// 输出:5ms(↑640x 性能提升!)

输出:explain() 诊断发现 COLLSCAN → 创建复合索引 → IXSCAN,查询从 3200ms 降到 5ms。

▶ 示例 2:综合性能调优实战(连接池 + 索引 + lean + 监控)

JAVASCRIPT
// === 场景:商品列表 API 性能优化(3s → 50ms)===

// === 优化前(慢)===
app.get('/api/products', async (req, res) => {
  const products = await Product.find();  // 全表扫描 + 返回所有字段
  res.json(products);
});
// 100 万文档,~3000ms,~50MB 数据

// === 优化后(快)===

// 1. 启用 mongoose debug(开发时观察查询)
mongoose.set('debug', (coll, method, query) => {
  console.log(`${coll}.${method}(${JSON.stringify(query)})`);
});

// 2. 优化连接池
mongoose.connect(uri, {
  maxPoolSize: 50,         // 根据并发调整
  minPoolSize: 5,
  maxIdleTimeMS: 30000,
  waitQueueTimeoutMS: 10000
});

// 3. 启用慢查询日志(>100ms)
mongoose.connection.db.admin().command({
  setParameter: 1,
  slowms: 100
});

// 4. 创建合适的索引
db.products.createIndex({ category: 1, isActive: 1, createdAt: -1 });
db.products.createIndex({ sku: 1 }, { unique: true });
db.products.createIndex({ title: 'text', description: 'text' });

// 5. 优化查询: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')  // 投影:仅返回 5 个字段
      .hint({ category: 1, isActive: 1, createdAt: -1 })  // 强制索引
      .sort({ createdAt: -1 })
      .skip((page - 1) * limit)
      .limit(Math.min(+limit, 100))
      .lean(),  // 跳过 mongoose hydrate
    Product.countDocuments(query)
  ]);

  res.json({
    success: true,
    data: products,
    meta: { page: +page, limit: +limit, total, pages: Math.ceil(total / limit) }
  });
});
// 100 万文档,~50ms(性能 ↑60x),~200KB 数据(减少 99.6%)

// === 6. explain() 验证索引生效 ===
const explain = await Product.find({ category: 'Electronics', isActive: true })
  .sort({ createdAt: -1 })
  .limit(20)
  .explain('executionStats');

console.log('Stage:', explain.queryPlanner.winningPlan.stage);
// 输出:IXSCAN(使用了索引)

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. 监控告警 ===
// 7.1 监控连接数
const connStatus = await mongoose.connection.db.admin().command({ serverStatus: 1 });
if (connStatus.connections.current > 1000) {
  console.warn(`⚠️  连接数过高: ${connStatus.connections.current}`);
  // 发送告警(邮件/钉钉/Slack)
}

// 7.2 监控慢查询
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 索引使用率
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(`⚠️  未使用索引: ${i.name}`);
  // 自动删除(生产环境谨慎)
  // await mongoose.connection.db.collection('products').dropIndex(i.name);
});

// === 8. APM 集成(Datadog/New Relic)===
const tracer = require('dd-trace').init();
tracer.use('mongoose', { service: 'shopdb' });
// 所有 mongoose 查询自动追踪,性能数据上报 Datadog

输出:连接池 + 索引 + lean + projection 综合优化,查询性能从 3s 降到 50ms(↑60x),数据传输减少 99.6%。

▶ 示例 3:慢查询日志 + Profiler + 索引使用分析

性能优化的第一步是定位问题——哪个查询慢?为什么慢?是缺索引还是索引没用上?MongoDB 提供三层诊断工具:慢查询日志(mongod 层记录)、Database Profiler(详细记录查询计划)、$indexStats(索引使用统计)。本示例实现完整的慢查询诊断流水线:发现 → 分析 → 优化 → 验证。

JAVASCRIPT
const mongoose = require('mongoose');

// === 1. 开启 Profiler(三种模式)===
// 模式 0:关闭(默认);模式 1:只记录慢查询;模式 2:记录所有操作
await mongoose.connection.db.adminCommand({
  profile: 1,                    // 模式 1:只记录慢查询
  slowms: 50,                    // 慢查询阈值 50ms
  sampleRate: 1                  // 采样率 100%
});

// === 2. 查询 Profiler 数据(system.profile 集合)===
async function getSlowQueries(minutes = 30) {
  const since = new Date(Date.now() - minutes * 60 * 1000);
  const slowQueries = await mongoose.connection.db
    .collection('system.profile')
    .find({
      ts: { $gte: since },
      op: { $in: ['query', 'update', 'remove', 'getmore'] },
      'command.aggregate': { $exists: false }  // 排除聚合(另分析)
    })
    .sort({ millis: -1 })
    .limit(20)
    .toArray();

  return slowQueries.map(q => ({
    timestamp: q.ts,
    operation: q.op,
    collection: q.ns,
    query: JSON.stringify(q.query || q.command),
    durationMs: q.millis,
    docsExamined: q.docsExamined,
    docsReturned: q.nreturned,
    indexUsed: q.planSummary,
    efficiency: q.docsExamined > 0
      ? (q.nreturned / q.docsExamined * 100).toFixed(1) + '%'
      : 'N/A'
  }));
}

// === 3. 索引使用统计 ===
async function analyzeIndexUsage(collectionName) {
  const stats = await mongoose.connection.db
    .collection(collectionName)
    .aggregate([{ $indexStats: {} }])
    .toArray();

  return stats.map(s => ({
    name: s.name,
    accesses: s.accesses.ops,
    since: s.accesses.since,
    isUnused: s.accesses.ops === 0,
    keys: Object.keys(s.key),
    size: s.size || 'N/A'
  }));
}

// === 4. explain() 诊断工具函数 ===
async function diagnoseQuery(collection, query, sort = {}) {
  const explanation = await mongoose.connection.db
    .collection(collection)
    .find(query)
    .sort(sort)
    .explain('executionStats');

  const stage = explanation.queryPlanner.winningPlan;
  const stats = explanation.executionStats;

  return {
    totalDocsExamined: stats.totalDocsExamined,
    totalDocsReturned: stats.totalDocsReturned,
    executionTimeMs: stats.executionTimeMillis,
    indexUsed: stage.inputStage?.indexName || 'COLLSCAN',
    isCollectionScan: stage.stage === 'COLLSCAN',
    efficiency: stats.totalDocsExamined > 0
      ? (stats.totalDocsReturned / stats.totalDocsExamined * 100).toFixed(1) + '%'
      : 'N/A',
    recommendation: getRecommendation(stage, stats)
  };
}

function getRecommendation(stage, stats) {
  const ratio = stats.totalDocsReturned / Math.max(stats.totalDocsExamined, 1);
  if (stage.stage === 'COLLSCAN') return '缺少索引!添加匹配查询条件的索引';
  if (ratio < 0.01) return '索引效率低!查询扫描了大量文档但返回很少,考虑更精确的索引';
  if (stats.executionTimeMillis > 100) return '查询仍较慢,考虑添加覆盖索引或优化排序';
  return '查询性能良好';
}

// === 5. 自动索引建议 ===
async function suggestIndexes(collectionName) {
  const slowQueries = await mongoose.connection.db
    .collection('system.profile')
    .find({ ns: `${mongoose.connection.name}.${collectionName}` })
    .sort({ millis: -1 })
    .limit(10)
    .toArray();

  const fieldFrequency = {};
  slowQueries.forEach(q => {
    const fields = q.query ? Object.keys(q.query) : [];
    fields.forEach(f => { fieldFrequency[f] = (fieldFrequency[f] || 0) + 1; });
  });

  const sortedFields = Object.entries(fieldFrequency)
    .sort((a, b) => b[1] - a[1])
    .map(([field, count]) => ({ field, queryCount: count }));

  return {
    collection: collectionName,
    topQueryFields: sortedFields.slice(0, 5),
    suggestedIndex: sortedFields.slice(0, 3).map(f => f.field).reduce((idx, f) => {
      idx[f] = 1; return idx;
    }, {})
  };
}

// === 6. 定时报告 ===
setInterval(async () => {
  const slowQueries = await getSlowQueries(5);
  if (slowQueries.length > 0) {
    console.log(`⚠️ 最近 5 分钟慢查询 (${slowQueries.length} 条):`);
    slowQueries.forEach(q => {
      console.log(`  ${q.collection} | ${q.durationMs}ms | 效率: ${q.efficiency} | 索引: ${q.indexUsed}`);
    });
  }
}, 5 * 60 * 1000);

输出:Profiler 自动记录 >50ms 的慢查询,diagnoseQuery 用 explain() 分析查询计划(COLLSCAN/IXSCAN),analyzeIndexUsage 找出未使用索引,suggestIndexes 根据慢查询频率自动建议索引。定时报告每 5 分钟汇总慢查询。

性能诊断的工作流:1. 开启 Profiler(模式 1 + slowms: 50)收集慢查询;2. 用 explain('executionStats') 分析最慢的查询——确认是 COLLSCAN(缺索引)还是低效索引(扫描太多文档);3. 添加索引后重新 explain() 验证;4. 用 $indexStats 定期检查未使用索引并清理——未使用索引浪费内存和写入性能;5. 线上慎用 Profiler 模式 2(记录所有操作有性能开销),用模式 1 + 合理 slowms 即可。

❓ 常见问题

Q maxPoolSize 越大越好?
A 不是。过大会耗尽数据库连接(MongoDB 默认 maxIncomingConnections = 65536)。
Q lean() 有什么副作用?
A 失去 mongoose 文档方法(save / populate)。仅适用于纯查询 API。
Q 如何判断查询是否用了索引?
A query.explain('executionStats') 查看 stage:IXSCAN(有索引)/ COLLSCAN(全表扫描)。

📖 小节


📝 作业

  1. 基础题(⭐):用 lean() 优化商品列表 API,对比性能差异。
  2. 基础题(⭐):启用慢查询日志,分析 Top 10 慢查询。
  3. 进阶题(⭐⭐):用 hint 强制索引,对比 COLLSCAN vs IXSCAN 性能。
  4. 进阶题(⭐⭐):分析 $indexStats,找出 unused indexes 并删除。
  5. 挑战题(⭐⭐⭐):完整性能调优(连接池 + 索引 + lean + 监控),对比前后性能。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏