MongoDB分片集群慢查询节点快速定位实战
在MongoDB分片集群中排查慢查询时,一个常见困惑是:在mongos节点上无法查询到system.profile数据。请不要急于怀疑配置错误——这并非缺陷,而是架构设计使然。接下来我们将逐步解析,如何精确锁定导致整个集群性能下降的具体shard节点。
首先明确一点:在分片集群中,system.profile仅存在于各个mongod shard节点上。mongos并不执行任何查询逻辑,它只负责查询路由与结果聚合。因此,当你在mongos上执行db.setProfilingLevel()并收到{"ok": 0, "errmsg": "no profiling information a vailable"}时,无需担心——这是正常行为,因为mongos本身不执行查询,自然不会生成profile日志。
为什么在mongos上执行db.setProfilingLevel()无效?
简而言之:mongos并不执行查询操作,因此不会产生profile数据写入。
- 所有实际查询均在后台
mongod实例上执行,profile日志仅写入对应shard节点的system.profile集合 - config server与
mongos上的system.profile为空或不可用,即使连接也无法查询到业务查询记录 - 不要在
mongos上耗费时间创建索引或调整阈值——因为它根本不会写入profile数据
如何快速定位需要排查的shard节点?
首先需要明确查询具体发送到了哪些shard,然后逐一排查。不能仅凭猜测或仅根据节点负载高低直接连接。
- 在
mongos上执行sh.status(),密切关注目标集合的chunks分布与shards列表,记录host字符串(例如shard02/10.0.1.20:27018,10.0.1.21:27018格式) - 通过
db.getSiblingDB("config").shards.find()获取每个shard的完整地址列表,筛选出可疑节点——若日志中频繁出现"planSummary":"SHARD_MERGE",则该shard应作为重点排查对象 - 直连时优先选择secondary节点:
mongo --host 10.0.1.21:27018 -u user -p pwd --authenticationDatabase admin,以避免给primary增加额外负载
连接shard后必须执行的三项操作
刚建立连接后不要急于执行find。若不完成以下三项操作,查询可能速度缓慢、数据遗漏或根本无法获取结果。
- 立即创建索引:
db.system.profile.createIndex({ ts: -1 })。否则,find({millis: {$gte: 50}}).sort({ts: -1})会触发全表扫描,原本几秒钟的操作可能延长至几十秒 - 调低
slowms阈值:db.setProfilingLevel(1, {slowms: 50})。在分片环境中,单个shard的执行时间可能仅为10–20ms,但经过网络传输和结果合并后,总耗时可能超过300ms。若不降低阈值,这些“看似正常”的查询将无法被记录 - 配置完成后立即验证是否生效:
db.getProfilingStatus()应返回"was": 1且"slowms": 50,才表示已正确启用
如何解读查询结果以避免误判
不要仅关注millis数值。例如,一个耗时320ms的请求在mongos日志中可见,但在shard的system.profile中可能只显示几个millis: 7的记录——这属于正常现象,因为每个shard仅记录自身执行的部分。
- 重点筛选
docsExamined > nreturned * 10的记录。例如docsExamined: 50000, nreturned: 25表明索引未生效或已失效 - 检查
planSummary:若出现"COLLSCAN",或"IXSCAN"但keysExamined: 0,基本可判断为缺少索引 - 对疑似慢查询执行
explain("executionStats")复现,重点关注shards字段下列出的节点数量。如果列出多个节点,说明触发了广播查询——很可能是因为缺少分片键
一个常被忽视的细节:profile数据默认仅保留最近1MB或30分钟(取决于capped size)。等到问题出现再临时启用profiler,数据可能已被覆盖。最佳实践是在关键shard上持续启用profiling,并定期检查索引是否仍然存在。
