Skip to content

性能优化

查询优化

分析查询性能

explain

javascript
db.users.find({ name: '张三' }).explain('executionStats')

关键指标

指标说明
totalDocsExamined扫描文档数
totalKeysExamined扫描索引键数
nReturned返回文档数
executionTimeMillis执行时间

优化目标

totalDocsExamined ≈ nReturned
totalKeysExamined ≈ nReturned

查询优化建议

1. 使用索引

javascript
db.users.find({ name: '张三' })
db.users.createIndex({ name: 1 })

2. 限制返回字段

javascript
db.users.find({}, { name: 1, email: 1, _id: 0 })

3. 使用 limit

javascript
db.users.find().limit(100)

4. 避免使用 $where

javascript
db.users.find({ $where: 'this.age > 20' })

db.users.find({ age: { $gt: 20 } })

5. 批量操作

javascript
for (let i = 0; i < 1000; i++) {
    db.users.insertOne({ name: `user_${i}` })
}

let docs = []
for (let i = 0; i < 1000; i++) {
    docs.push({ name: `user_${i}` })
}
db.users.insertMany(docs)

慢查询分析

开启慢查询日志

javascript
db.setProfilingLevel(1, { slowms: 100 })

查看慢查询

javascript
db.system.profile.find().sort({ ts: -1 }).limit(10)

分析级别

级别说明
0关闭
1只记录慢查询
2记录所有操作
javascript
db.setProfilingLevel(0)
db.setProfilingLevel(1, { slowms: 50 })
db.setProfilingLevel(2)

索引优化

索引设计原则

1. ESR 原则

Equality → Sort → Range

javascript
db.users.find({ city: '北京' }).sort({ age: -1 }).skip(0).limit(10)

db.users.createIndex({ city: 1, age: -1 })

2. 覆盖索引

javascript
db.users.createIndex({ name: 1, email: 1 })

db.users.find(
    { name: '张三' },
    { _id: 0, name: 1, email: 1 }
)

3. 选择性高的字段

javascript
db.users.aggregate([
    {
        $group: {
            _id: '$status',
            count: { $sum: 1 }
        }
    }
])

索引监控

查看索引使用情况

javascript
db.users.aggregate([{ $indexStats: {} }])

查看未使用的索引

javascript
db.users.aggregate([
    { $indexStats: {} },
    { $match: { 'accesses.ops': 0 } }
])

删除无用索引

javascript
db.users.dropIndex('unused_index_name')

写入优化

批量写入

javascript
db.users.insertMany([
    { name: '张三' },
    { name: '李四' },
    { name: '王五' }
])

有序 vs 无序

javascript
db.users.insertMany([...], { ordered: false })

写关注调整

javascript
db.users.insertOne(
    { name: '张三' },
    { writeConcern: { w: 1 } }
)

使用 bulkWrite

javascript
db.users.bulkWrite([
    { insertOne: { document: { name: '用户A' } } },
    { updateOne: { filter: { name: '张三' }, update: { $set: { age: 25 } } } },
    { deleteOne: { filter: { name: '李四' } } }
], { ordered: false })

内存优化

WiredTiger 缓存

yaml
storage:
  wiredTiger:
    engineConfig:
      cacheSizeGB: 4

查看内存使用

javascript
db.serverStatus().wiredTiger.cache

缓存指标

指标说明
bytes currently in the cache缓存使用量
maximum bytes configured最大缓存配置
pages read into cache读入缓存的页数
pages written from cache从缓存写出的页数

内存优化建议

  1. 设置合适的缓存大小(通常为内存的 50%)
  2. 避免大文档
  3. 使用投影减少返回数据
  4. 合理使用索引

连接池优化

连接池配置

javascript
const { MongoClient } = require('mongodb')

const client = new MongoClient(uri, {
    maxPoolSize: 100,
    minPoolSize: 10,
    maxIdleTimeMS: 30000,
    waitQueueTimeoutMS: 5000
})

连接池参数

参数说明
maxPoolSize最大连接数
minPoolSize最小连接数
maxIdleTimeMS空闲超时时间
waitQueueTimeoutMS等待超时时间

查看连接状态

javascript
db.serverStatus().connections

存储优化

文档大小优化

避免大文档

javascript
db.articles.insertOne({
    title: '文章标题',
    content: '很长的内容...'
})

db.articles.insertOne({
    title: '文章标题',
    content_id: ObjectId('...')
})

字段名缩短

javascript
db.users.insertOne({
    username: '张三',
    emailAddress: 'test@example.com'
})

db.users.insertOne({
    u: '张三',
    e: 'test@example.com'
})

压缩

yaml
storage:
  wiredTiger:
    collectionConfig:
      blockCompressor: snappy
    indexConfig:
      prefixCompression: true

压缩算法

算法说明
none不压缩
snappy快速压缩(默认)
zlib高压缩比
zstd高性能压缩

分片优化

分片键选择

选择原则

  1. 高基数(Cardinality)
  2. 低频率(Frequency)
  3. 单调性变化

分片键类型

类型说明
范围分片按范围分布
哈希分片均匀分布
javascript
sh.shardCollection('mydb.users', { _id: 'hashed' })
sh.shardCollection('mydb.orders', { user_id: 1 })

分片监控

javascript
sh.status()
db.users.getShardDistribution()

均衡器

javascript
sh.stopBalancer()
sh.startBalancer()
sh.setBalancerState(false)

监控工具

serverStatus

javascript
db.serverStatus()
db.serverStatus().connections
db.serverStatus().opcounters
db.serverStatus().memory

dbStats

javascript
db.stats()
db.stats(1024 * 1024)

collectionStats

javascript
db.users.stats()
db.users.stats({ scale: 1024 })

currentOp

javascript
db.currentOp()
db.currentOp({ 'active': true, 'secs_running': { $gt: 5 } })

终止操作

javascript
db.killOp(opId)

性能分析工具

MongoDB Compass

图形化界面工具,支持:

  • 查询构建
  • 索引分析
  • 性能监控

mongostat

bash
mongostat --host localhost --port 27017
mongostat 5

mongotop

bash
mongotop --host localhost --port 27017
mongotop 5

Atlas Performance Advisor

MongoDB Atlas 云服务提供的性能建议工具。

性能优化清单

查询优化

  • [ ] 使用 explain 分析慢查询
  • [ ] 创建合适的索引
  • [ ] 使用覆盖索引
  • [ ] 限制返回字段
  • [ ] 使用 limit 限制结果

索引优化

  • [ ] 遵循 ESR 原则
  • [ ] 删除未使用的索引
  • [ ] 使用复合索引
  • [ ] 考虑部分索引

写入优化

  • [ ] 使用批量写入
  • [ ] 调整写关注级别
  • [ ] 使用无序写入

内存优化

  • [ ] 设置合适的缓存大小
  • [ ] 监控内存使用
  • [ ] 优化文档大小

连接优化

  • [ ] 配置连接池
  • [ ] 监控连接数
  • [ ] 设置超时时间

常见性能问题

1. 全表扫描

javascript
db.users.find({ name: '张三' }).explain()

db.users.createIndex({ name: 1 })

2. 内存排序

javascript
db.users.find().sort({ age: -1 }).explain()

db.users.createIndex({ age: -1 })

3. 连接数过多

javascript
db.serverStatus().connections

db.adminCommand({ setParameter: 1, maxIncomingConnections: 1000 })

4. 写入性能低

  • 使用批量写入
  • 调整写关注级别
  • 检查索引数量

5. 查询超时

javascript
db.users.find({ name: '张三' }).maxTimeMS(5000)

下一步学习