外观
MongoDB 性能故障
常见性能故障类型
1. 慢查询
症状:
- 查询响应时间超过预期阈值
- 应用程序延迟增加
- 数据库负载升高
常见原因:
- 缺少合适的索引
- 索引使用不当
- 全集合扫描
- 复杂的聚合操作
- 大量数据排序
- 锁竞争
诊断方法:
javascript
// 查看慢查询日志
db.system.profile.find({
millis: { $gt: 100 } // 查找超过100ms的查询
}).sort({ millis: -1 }).limit(10)
// 分析查询执行计划
db.collection.find({ query: "condition" }).explain("executionStats")
// 查看当前操作
db.currentOp({
$query: { "active": true, "millis": { $gt: 500 } }
})解决方案:
- 创建合适的索引
- 优化查询条件
- 避免全集合扫描
- 使用覆盖索引
- 限制返回数据量
- 优化聚合管道
2. 高CPU使用率
症状:
- CPU使用率持续高于80%
- 查询响应时间增加
- 系统负载升高
常见原因:
- 大量复杂查询
- 频繁的索引扫描
- 全集合扫描
- 频繁的写操作
- 锁竞争
- 数据库压缩/解压操作
诊断方法:
javascript
// 查看当前操作
db.currentOp({ $query: { "active": true } })
// 查看慢查询
db.system.profile.find().sort({ millis: -1 })
// 查看索引使用情况
db.collection.aggregate([{ $indexStats: {} }])解决方案:
- 优化查询和索引
- 增加服务器资源
- 分片集群扩展
- 优化写操作
- 减少锁竞争
3. 内存不足
症状:
- 内存使用率接近或达到100%
- 频繁的页面置换
- 磁盘I/O增加
- 查询性能下降
常见原因:
- 数据量超过内存容量
- 索引过大
- 缓存配置不当
- 大量并发连接
- 内存泄漏
诊断方法:
javascript
// 查看内存使用情况
db.serverStatus().mem
// 查看缓存使用情况
db.serverStatus().wiredTiger.cache
// 查看连接数
db.serverStatus().connections
// 查看索引大小
db.collection.totalIndexSize()解决方案:
- 增加内存资源
- 优化索引
- 调整缓存大小
- 限制连接数
- 分片集群扩展
4. 磁盘I/O 瓶颈
症状:
- 磁盘I/O使用率高
- 读写延迟增加
- 查询和写操作变慢
- 系统响应时间增加
常见原因:
- 磁盘性能不足
- 大量写操作
- 频繁的索引构建
- 全集合扫描
- 日志写入频繁
诊断方法:
bash
# 使用iostat监控磁盘I/O
iostat -x 1
# 查看MongoDB I/O统计
db.serverStatus().wiredTiger.concurrentTransactions
db.serverStatus().wiredTiger.cache解决方案:
- 使用SSD存储
- 优化查询和索引
- 调整写关注级别
- 分离日志和数据文件
- 分片集群扩展
5. 连接数过多
症状:
- 连接数接近或达到最大限制
- 新连接被拒绝
- 服务器内存使用率高
- 系统响应变慢
常见原因:
- 应用程序连接池配置不当
- 大量并发请求
- 连接泄漏
- 长连接未关闭
诊断方法:
javascript
// 查看当前连接数
db.serverStatus().connections
// 查看连接限制
db.adminCommand({getCmdLineOpts: 1}).parsed.net.maxIncomingConnections
// 查看当前操作的连接
db.currentOp().inprog.forEach(function(op) {
if (op.client) {
print(op.client + " - " + op.desc);
}
});解决方案:
- 优化连接池配置
- 增加连接限制
- 修复连接泄漏
- 使用短连接或连接池
- 分片集群扩展
6. 锁竞争
症状:
- 大量操作等待锁
- 事务执行时间长
- 系统吞吐量下降
- 锁等待百分比高
常见原因:
- 频繁的写操作
- 长事务
- 热点数据
- 不合适的隔离级别
诊断方法:
javascript
// 查看锁统计
db.serverStatus().locks
// 查看当前操作的锁信息
db.currentOp({
$query: { "waitingForLock": true }
})
// 查看锁等待时间长的操作
db.currentOp({
$query: { "active": true, "locks": { $exists: true } }
})解决方案:
- 优化写操作
- 减少事务范围
- 避免长事务
- 使用更细粒度的锁
- 分散热点数据
7. 复制延迟
症状:
- 从节点复制延迟增加
- 读写分离性能下降
- 数据不一致风险
常见原因:
- 主节点负载过高
- 网络延迟
- 从节点资源不足
- 大量写操作
- 索引构建
诊断方法:
javascript
// 查看复制延迟
rs.printSecondaryReplicationInfo()
// 查看复制状态
rs.status()
// 查看 oplog 状态
db.printReplicationInfo()解决方案:
- 增加从节点资源
- 优化网络连接
- 调整 oplog 大小
- 优化写操作
- 使用优先级调整
8. 分片集群不平衡
症状:
- 分片间数据分布不均
- 某些分片负载过高
- 查询性能下降
- 迁移操作频繁
常见原因:
- 分片键选择不当
- 数据倾斜
- 块大小设置不当
- 均衡器配置问题
诊断方法:
javascript
// 查看分片状态
sh.status()
// 查看集合的块分布
sh.status({ verbose: true })
// 查看均衡器状态
sh.getBalancerState()解决方案:
- 优化分片键设计
- 手动迁移块
- 调整块大小
- 优化均衡器配置
- 重新分片
性能故障诊断工具
1. MongoDB 内置工具
mongostat
实时监控MongoDB状态:
bash
# 实时监控,每秒输出一次
mongostat
# 监控指定数据库
mongostat --uri "mongodb://localhost:27017/mydb"
# 每5秒输出一次
mongostat 5mongotop
监控集合读写活动:
bash
# 实时监控
mongotop
# 每3秒输出一次
mongotop 3db.currentOp()
查看当前操作:
javascript
// 查看所有活动操作
db.currentOp({ $query: { "active": true } })
// 查看等待锁的操作
db.currentOp({ $query: { "waitingForLock": true } })
// 查看长时间运行的操作
db.currentOp({ $query: { "active": true, "millis": { $gt: 500 } } })db.serverStatus()
查看服务器状态:
javascript
// 查看服务器状态
db.serverStatus()
// 查看内存使用情况
db.serverStatus().mem
// 查看连接数
db.serverStatus().connections
// 查看锁统计
db.serverStatus().locks2. 第三方监控工具
MongoDB Atlas
- 提供全面的监控和告警
- 实时性能指标
- 慢查询分析
- 自动索引建议
Prometheus + Grafana
- 自定义监控仪表盘
- 灵活的告警规则
- 长期指标存储
- 支持多数据源
Datadog
- 全栈监控
- 自动仪表盘
- 智能告警
- 分布式追踪
New Relic
- 应用性能监控
- 数据库监控
- 分布式追踪
- 智能告警
性能故障排查流程
1. 收集信息
- 确认故障症状和影响范围
- 收集系统和数据库监控数据
- 查看慢查询日志
- 检查当前操作
- 分析服务器资源使用情况
2. 定位问题
- 确定故障类型(慢查询、高CPU、内存不足等)
- 识别有问题的查询或操作
- 分析查询执行计划
- 检查索引使用情况
- 查看锁竞争情况
3. 分析原因
- 确定根本原因
- 评估影响范围
- 考虑可能的解决方案
4. 实施解决方案
- 优先实施影响最小的解决方案
- 监控解决方案效果
- 必要时调整方案
性能故障预防措施
1. 设计阶段
- 合理设计数据模型
- 选择合适的索引策略
- 设计合适的分片键
- 考虑数据增长
2. 部署阶段
- 选择合适的硬件配置
- 优化MongoDB配置
- 配置监控和告警
- 实施备份和恢复策略
3. 运行阶段
- 定期监控性能指标
- 分析慢查询
- 优化查询和索引
- 定期维护索引
- 监控资源使用情况
4. 扩展阶段
- 考虑分片集群扩展
- 实施读写分离
- 优化连接池配置
- 考虑垂直扩展
性能故障案例分析
案例1:缺少索引导致慢查询
症状:
- 订单查询响应时间超过5秒
- CPU使用率高
诊断:
javascript
// 查看慢查询
db.system.profile.find({ ns: "shop.orders", millis: { $gt: 1000 } }).sort({ millis: -1 })
// 分析执行计划
db.orders.find({ customerId: ObjectId("507f1f77bcf86cd799439011") }).explain("executionStats")
// 结果显示 COLLSCAN(全集合扫描)解决方案:
javascript
// 创建索引
db.orders.createIndex({ customerId: 1 })效果:
- 查询响应时间从5秒降低到50ms
- CPU使用率恢复正常
案例2:内存不足导致性能下降
症状:
- 内存使用率持续接近100%
- 磁盘I/O增加
- 查询性能下降
诊断:
javascript
// 查看内存使用情况
db.serverStatus().mem
// 结果显示 resident 内存接近系统总内存
// 查看缓存使用情况
db.serverStatus().wiredTiger.cache
// 结果显示脏数据比例高,缓存使用率接近100%
// 查看索引大小
db.collection.totalIndexSize()
// 结果显示索引大小超过可用内存解决方案:
- 增加服务器内存
- 优化索引,删除未使用的索引
- 调整WiredTiger缓存大小
- 考虑分片集群扩展
效果:
- 内存使用率降低到60%
- 磁盘I/O减少
- 查询性能恢复正常
性能故障应急响应
1. 紧急故障处理流程
- 快速响应:立即响应性能故障告警
- 初步诊断:使用mongostat、mongotop等工具快速定位问题
- 缓解措施:
- 终止长时间运行的操作
- 调整查询或索引
- 增加资源(如果可能)
- 根本原因分析:深入分析故障原因
- 永久修复:实施长期解决方案
- 验证和总结:验证修复效果,总结经验
2. 常见应急命令
javascript
// 终止长时间运行的操作
db.killOp(opid)
// 查看当前操作,获取opid
db.currentOp()
// 临时调整日志级别
db.setLogLevel(0, "query")
// 查看慢查询
db.system.profile.find().sort({ millis: -1 })性能故障监控与告警
1. 关键监控指标
| 指标类型 | 关键指标 | 告警阈值 |
|---|---|---|
| CPU | CPU使用率 | >80% 持续5分钟 |
| 内存 | 内存使用率 | >90% 持续5分钟 |
| 磁盘 | 磁盘I/O使用率 | >80% 持续5分钟 |
| 连接 | 连接数 | >80% 最大连接数 |
| 查询 | 慢查询数量 | >10 个/分钟 |
| 复制 | 复制延迟 | >30秒 |
| 锁 | 锁等待时间 | >1秒 持续5分钟 |
2. 告警配置
MongoDB Atlas 告警
- 配置基于阈值的告警
- 支持多种通知渠道(邮件、短信、webhook等)
- 支持自定义告警规则
Prometheus + Alertmanager
- 配置PromQL告警规则
- 支持多种通知渠道
- 支持告警分组和抑制
第三方监控工具
- Datadog:智能告警
- New Relic:异常检测
- Zabbix:基于阈值的告警
常见问题(FAQ)
Q1: 如何快速定位慢查询?
A1: 可以使用以下方法:
- 启用慢查询日志:
db.setProfilingLevel(1, { slowms: 100 }) - 使用mongostat实时监控
- 使用db.currentOp()查看当前操作
- 分析查询执行计划
Q2: 如何处理大量慢查询?
A2: 处理大量慢查询的步骤:
- 分析慢查询模式
- 创建或优化索引
- 优化查询语句
- 考虑反规范化设计
- 增加服务器资源或分片扩展
Q3: 高CPU使用率的常见原因是什么?
A3: 高CPU使用率的常见原因:
- 大量复杂查询
- 频繁的索引扫描
- 全集合扫描
- 频繁的写操作
- 锁竞争
- 数据库压缩/解压操作
Q4: 如何处理复制延迟?
A4: 处理复制延迟的方法:
- 增加从节点资源
- 优化网络连接
- 调整oplog大小
- 优化写操作
- 使用优先级调整
- 考虑使用更强大的硬件
Q5: 如何预防性能故障?
A5: 预防性能故障的措施:
- 合理设计数据模型和索引
- 优化查询和写操作
- 配置合适的监控和告警
- 定期进行性能测试
- 实施良好的运维实践
- 考虑适当的扩展策略
Q6: 如何选择合适的分片键?
A6: 选择合适分片键的原则:
- 高基数:有足够多的唯一值
- 低频率更新:避免频繁更新分片键
- 均匀分布:数据分布均匀
- 符合查询模式:支持常用查询
- 避免热点:避免单一分片负载过高
Q7: 如何处理数据倾斜?
A7: 处理数据倾斜的方法:
- 优化分片键设计
- 手动迁移块
- 使用标签分片
- 考虑重新分片
- 优化查询模式
Q8: 如何优化写操作性能?
A8: 优化写操作性能的方法:
- 使用批量写入
- 调整写关注级别
- 优化索引
- 减少索引数量
- 增加服务器资源
- 考虑分片集群扩展
Q9: 如何优化聚合查询?
A9: 优化聚合查询的方法:
- 尽早使用$match和$sort
- 为聚合查询创建合适的索引
- 避免在聚合管道中使用$unwind
- 使用$out或$merge存储结果
- 考虑使用MapReduce的替代方案
Q10: 如何监控MongoDB性能?
A10: 监控MongoDB性能的方法:
- 使用MongoDB内置工具(mongostat、mongotop、db.serverStatus())
- 使用MongoDB Atlas(云服务)
- 使用Prometheus + Grafana
- 使用第三方监控工具(Datadog、New Relic等)
- 配置合适的告警规则
