慢查询
一、认识
本文介绍了较为全面的 MongoDB
慢查询分析和优化方案, 从慢查询诊断、原因定位、索引与查询优化、数据模型调整到硬件和集群层面的优化,帮助你系统地解决性能瓶颈问题。
二、慢查询分析
2.1 MongoDB Profiler
利用 MongoDB
内置的 Profiler
记录慢查询日志,通过查询 system.profile
集合或者直接分析 mongod
日志获取详细信息。具体操作如下:
-
开启慢查询日志: 在
MongoDB
配置文件或启动参数中设置slowms
(默认100ms
),记录执行时间超过此值的查询。Profiler
可以帮助捕捉运行中的查询,确定哪些操作耗时较长。可以通过设置不同的级别: 级别0
, 关闭profiler
; 级别 1, 仅捕获慢查询; 级别 2, 捕获所有操作(仅在测试环境使用,生产环境慎用)// 开启 profiler,只记录超过100ms的查询
db.setProfilingLevel(1, { slowms: 100 }); -
查看慢查询日志: 慢查询信息会写入
mongod
日志中,也可以通过查询system.profile
集合查看db.system.profile.find({ millis: { $gt: 100 } }).sort({ millis: -1 }).limit(10).pretty();
2.2 MongoDb explain
在查询前加上 .explain("executionStats")
可以详细显示查询的执行计划、扫描的文档数、使用的索引等信息:
db.collection.find({ field: value }).explain("executionStats");
-
winningPlan
: 最终使用的执行计划类型(如COLLSCAN
表示全表扫描,IXSCAN
表示索引扫描) -
totalKeysExamined
与totalDocsExamined
: 二者差距较大提示索引命中率不佳 -
executionTimeMillis
: 实际耗时
2.3 MongoDB Atlas Performance Advisor
三、慢查询优化
3.1 索引优化
创建合适的单字段/复合索引, 尽量让索引覆盖查询(即查询字段和返回字段均在索引中), 避免回表查找。利用 db.collection.getIndexes()
检查已有索引,定期清理冗余索引,确保最佳匹配。
3.2 查询优化
一、重构查询条件: 避免使用无法利用索引的操作符(如正则表达式前置通配符、$where
),改为精确查询。
二、限制返回字段: 使用 projection
限制返回的字段,减少网络传输和内存占用
3.3 聚合优化
聚合管道 Aggregation Pipeline
优化: 优化的目标是降低管道中数据传输的数量, 移动 $match
以减少不必要的数据处理, 合并可以合并的阶段, 让索引尽可能早地生效。虽然, 我们 MongoDB
内部已经做了许多优化工作, 我们可以基于 MongoDB
内部的优化策略, 我们在代码中就就已经将优化做到极致, 这样我们不仅可以节省 MongoDB
进一步的优化工作, 也提高了我们的代码质量。可以通过 db.collection.aggregate()
方法中添加 explain
选项, 查看优化后的管道。 我们可以在阶段顺序上, $match
应尽早执行,减少后续阶段的数据量, $group
尽量靠后,避免不必要的计算, $project
一般在管道的最后,控制返回的字段; 合并可合并的阶段; 另外, 我们可以增加索引, 确保 $match
和 $lookup
关联字段有索引,否则会导致全表扫描; 我们可以使用 $facet
来实现并行统计, 提高查询效率。在 MongoDB
中,$facet
是聚合管道的一个阶段,它允许你在同一个聚合查询中并行运行多个子管道, 可以并行处理, 每个子管道可以独立处理相同的一组输入文档,并生成各自的输出。这样你就可以一次性获得多种不同角度的聚合结果,而无需多次查询。 注意: $facet
在管道中的第一个阶段, 不会使用索引, 所以, 不要将 $facet
放到第一阶段。