Skip to content

性能优化

MongoDB性能优化是数据库管理中的重要环节。本章将介绍查询优化、索引优化、服务器配置优化等方面的知识。

性能优化概述

优化目标

javascript
// MongoDB性能优化的主要目标:

// 1. 缩短查询响应时间
// 2. 提高系统吞吐量
// 3. 降低资源消耗
// 4. 提升用户体验

// 优化原则:
// 1. 先诊断问题,再进行优化
// 2. 优化要有针对性,避免过度优化
// 3. 优化后要验证效果
// 4. 记录优化过程和结果

优化层次

javascript
// MongoDB性能优化可以分为多个层次:

// 1. 查询层优化
//    优化查询语句、使用索引

// 2. 数据模型优化
//    文档结构设计、嵌套vs引用

// 3. 索引优化
//    索引设计、索引维护

// 4. 服务器配置优化
//    内存、连接数、缓存配置

// 5. 架构优化
//    复制集、分片集群

查询优化

使用explain分析查询

javascript
// explain是分析查询执行计划的重要工具

// 基本用法
db.users.find({ name: "张三" }).explain()

// 详细模式
db.users.find({ name: "张三" }).explain("executionStats")

// 全部信息
db.users.find({ name: "张三" }).explain("allPlansExecution")

// 输出关键字段说明:
// - winningPlan: 被选中的执行计划
// - stage: 执行阶段
//   - COLLSCAN: 全表扫描
//   - IXSCAN: 索引扫描
//   - FETCH: 获取文档
// - indexName: 使用的索引名称
// - executionTimeMillis: 执行时间(毫秒)
// - totalDocsExamined: 扫描的文档数
// - nReturned: 返回的文档数

// 判断是否使用索引
var plan = db.users.find({ name: "张三" }).explain()
if (plan.queryPlanner.winningPlan.stage === "COLLSCAN") {
    print("全表扫描,未使用索引")
} else if (plan.queryPlanner.winningPlan.stage === "IXSCAN") {
    print("使用了索引")
}

查询优化技巧

javascript
// 查询优化技巧

// 1. 避免全表扫描
// 使用索引覆盖查询条件

// 2. 使用投影减少返回数据
db.users.find({ status: "active" }, { name: 1, email: 1, _id: 0 })

// 3. 使用limit限制结果集
db.users.find({ status: "active" }).limit(100)

// 4. 避免使用$where
// $where会执行JavaScript,性能较差

// 5. 避免使用$regex前缀通配符
// 不推荐
db.users.find({ name: // })  // 无法使用索引
// 推荐
db.users.find({ name: /^/ }) // 可以使用索引

// 6. 使用hint强制使用索引
db.users.find({ name: "张三" }).hint({ name: 1 })

// 7. 批量操作代替循环操作
// 不推荐
for (var i = 0; i < 1000; i++) {
    db.users.insertOne({ name: "user" + i })
}
// 推荐
var docs = []
for (var i = 0; i < 1000; i++) {
    docs.push({ name: "user" + i })
}
db.users.insertMany(docs)

覆盖索引

javascript
// 覆盖索引:查询的所有字段都在索引中,不需要回表

// 创建复合索引
db.users.createIndex({ name: 1, email: 1 })

// 使用覆盖索引查询
db.users.find(
    { name: "张三" },
    { _id: 0, name: 1, email: 1 }  // 只返回索引字段
)

// 验证是否使用覆盖索引
var plan = db.users.find(
    { name: "张三" },
    { _id: 0, name: 1, email: 1 }
).explain("executionStats")

// 如果totalDocsExamined为0,说明使用了覆盖索引
print("扫描文档数: " + plan.executionStats.totalDocsExamined)

索引优化

索引设计原则

javascript
// 索引设计原则

// 1. ESR原则(Equality-Sort-Range)
// 等值查询字段 -> 排序字段 -> 范围查询字段

// 示例查询
db.orders.find({ status: "completed" })
    .sort({ createdAt: -1 })
    .limit(100)

// 推荐索引
db.orders.createIndex({ status: 1, createdAt: -1 })

// 2. 选择性高的字段优先
// 选择性 = 不同值数量 / 总文档数
// 选择性越高,索引效果越好

// 3. 避免过多索引
// 索引占用存储空间,影响写入性能
// 建议单集合索引不超过5-10个

// 4. 使用复合索引代替多个单字段索引

// 5. 定期检查索引使用情况
db.users.aggregate([
    { $indexStats: {} }
])

索引维护

javascript
// 索引维护操作

// 查看索引使用情况
db.users.aggregate([
    { $indexStats: {} },
    { $match: { accesses: { $eq: 0 } } }  // 查找未使用的索引
])

// 删除未使用的索引
db.users.dropIndex("unused_index")

// 重建索引(减少碎片)
db.users.reIndex()

// 查看索引大小
db.users.totalIndexSize()

// 后台创建索引(不阻塞操作)
db.users.createIndex({ name: 1 }, { background: true })

服务器配置优化

内存配置

yaml
# mongod.conf 内存相关配置

storage:
  # WiredTiger缓存大小(建议物理内存的50%-60%)
  wiredTiger:
    engineConfig:
      cacheSizeGB: 4

  # 日志配置
  journal:
    enabled: true

# 网络配置
net:
  # 最大连接数
  maxIncomingConnections: 65536

连接池配置

javascript
// 应用程序连接池配置(以Node.js为例)

const { MongoClient } = require('mongodb')

const client = new MongoClient('mongodb://localhost:27017', {
    // 连接池大小
    maxPoolSize: 100,
    // 最小连接数
    minPoolSize: 10,
    // 连接超时时间
    connectTimeoutMS: 30000,
    // Socket超时时间
    socketTimeoutMS: 360000,
    // 最大闲置时间
    maxIdleTimeMS: 60000,
    // 等待队列超时
    waitQueueTimeoutMS: 5000
})

监控与诊断

服务器状态

javascript
// 查看服务器状态
db.serverStatus()

// 关键指标:
// - connections: 连接信息
// - opcounters: 操作计数
// - memory: 内存使用
// - network: 网络统计
// - wiredTiger: 存储引擎统计

// 查看当前操作
db.currentOp()

// 查看慢操作
db.currentOp({
    "secs_running": { $gt: 5 }  // 运行超过5秒的操作
})

// 终止长时间运行的操作
db.killOp(opId)

数据库统计

javascript
// 查看数据库统计
db.stats()

// 查看集合统计
db.users.stats()

// 查看集合存储大小
db.users.storageSize()

// 查看集合索引大小
db.users.totalIndexSize()

性能分析

javascript
// 开启性能分析
db.setProfilingLevel(1, 50)  // 记录超过50ms的操作

// 查看分析级别
db.getProfilingLevel()

// 查看慢查询
db.system.profile.find().sort({ ts: -1 }).limit(10)

// 关闭性能分析
db.setProfilingLevel(0)

本章小结

本章介绍了MongoDB性能优化的相关知识:

  1. 优化概述:理解优化的目标和层次
  2. 查询优化:学会使用explain分析查询,掌握优化技巧
  3. 索引优化:掌握索引设计原则和维护方法
  4. 服务器配置:了解内存和连接池配置
  5. 监控诊断:学会使用各种监控工具诊断问题

下一章,我们将学习复制集,了解MongoDB的高可用架构。