开启Profiler应设为级别1且slowms=50,即db.setProfilingLevel(1, { slowms: 50 }),并配置mongod.conf中operationProfiling.mode: slowOp和slowOpThresholdMs: 50,再用db.getProfilingStatus()确认生效。

怎么开Profiler才不踩坑
Profiler不是一开就灵,开错级别或阈值反而掩盖问题、拖慢性能。默认关闭(profilingLevel: 0),盲目设成 2(记录所有操作)在生产环境等于自找麻烦——日志爆炸、写入延迟飙升、system.profile集合迅速膨胀。
-
db.setProfilingLevel(1, { slowms: 50 })是最常用且安全的起点:只捕获 >50ms 的查询,兼顾可观测性与开销 - 永久生效必须改配置文件(
mongod.conf),YAML 格式里写operationProfiling.mode: slowOp和slowOpThresholdMs: 50,别漏掉mode字段,否则配置不生效 - 开启后务必确认生效:
db.getProfilingStatus()返回{ "was": 1, "slowms": 50 }才算到位,别只信命令没报错就以为成功
从 system.profile 里揪出真凶语句
慢查询藏在 system.profile 集合里,但直接 db.system.profile.find() 会刷屏,且容易忽略关键线索。重点不是“有没有慢”,而是“为什么慢”。
- 先筛耗时:用
millis过滤,比如db.system.profile.find({ millis: { $gt: 100 } }).sort({ ts: -1 }).limit(5)查最近 5 条超 100ms 的 - 看
planSummary字段——COLLSCAN是红灯,说明没走索引;IXSCAN是绿灯,但还得看docsExamined和nreturned比值,如果扫描 10 万文档只返回 1 条,索引可能建得不对 - 注意
ns字段(如"martin.slow_test"),它标出具体库和集合,避免在错误集合上白忙活
日志文件里找更隐蔽的慢操作
Profiler 只抓数据库内操作,但真实瓶颈常在协议层或锁等待。MongoDB 日志(/var/log/mongodb/mongod.log)里藏着更底层的线索,尤其对写入、聚合、事务类慢操作。
- 用
grep "COMMAND.*[0-9]\+ms" /var/log/mongodb/mongod.log快速定位带耗时标记的操作行 - 盯紧
locks字段里的acquireCount,比如"Global": {"acquireCount": {"w": NumberLong(7814)}表示全局写锁争抢严重,可能是长事务或未释放的写操作堵住了路 -
planSummary在日志里也出现,但字段名缩写为IXSCAN/COLLSCAN,配合keysExamined和docsExamined能判断索引是否被有效利用
查到慢查询后,explain("executionStats") 是唯一出路
光看 system.profile 只知道“慢”,但不知道“卡在哪”。真正定位根因必须回原语句跑 explain(),它会暴露执行计划细节,比日志更准、更细。
- 对慢查询语句补上
.explain("executionStats"),例如:db.slow_test.find({"num": 9999}).explain("executionStats") - 重点看
executionStats.totalDocsExamined—— 如果远大于nReturned,基本确定是索引缺失或失效;若totalKeysExamined为 0,说明压根没命中索引 - 别信
executionStats.executionTimeMillis单次耗时,要结合多次执行看波动;高波动往往指向锁竞争或内存抖动,不是索引能解决的
Profiler 和日志只是探针,真正动手优化前,必须用 explain() 确认执行路径。很多人跳过这步,一上来就加索引,结果发现查询根本没走到新索引上——因为字段类型不匹配、条件用了 $regex 或函数,或者复合索引顺序错了。这些细节,日志不会告诉你,只有 explain 的 stage 和 filter 字段才说得清。










