mongodb慢查询和异常CPU占用排查 作者: ciniao 时间: 2019-04-26 分类: 技术 * 本文发布时间较久,请自行判断有效性 ####是否开启了慢查询记录 选择某一数据库,执行`db.getProfilingStatus()`,可得到如下结果:  was=1表示开启了慢查询记录,0表示未开启 showms表示记录超过多少ms的数据库操作 ####开启记录慢查询 只开启某一个数据库,选中某一数据库后,shell里执行`db.setProfilingLevel(1,200)`,其中,参数1表示记录的级别,参数2表示记录大于多少ms的操作 ``` 0:关闭,不收集任何数据。 1:收集慢查询数据,默认是100毫秒。 2:收集所有数据 ``` 默认情况下,开启的profile表只有1M储存空间,可通过下面命令调整到100M ``` db.setProfilingLevel(0); db.system.profile.drop(); db.createCollection( "system.profile", { capped: true, size:100000000 } ); db.setProfilingLevel(1,200); db.system.profile.stats(); ``` ####服务器上的所有数据库都开启 ``` #也可以MongoDB启动时,开启Profiling mongod --profile=1 --slowms=200 #或者在配置文件里添加 profile = 1 slowms = 200 ``` ####分析方法 查看system.profile表,里面会记录所有慢查询记录  其中一条的详细说明如下: ``` #下面是一个超过200ms的查询语句 { "op" : "query", #操作类型,有insert、query、update、remove、getmore、command "ns" : "F10data3.f10_2_8_3_jgcc", "query" : { #具体的查询语句 包括过滤条件,limit行数 排序字段 filter" : { "jzrq" : { "$gte" : ISODate("2017-03-31T16:00:00.000+0000"), "$lte" : ISODate("2017-06-30T15:59:59.000+0000") }, "jglxfldm" : 10.0 }, "ntoreturn" : 200.0, "sort" : { #如果有排序 则显示排序的字段 这里是 RsId "RsId" : 1.0 } }, "keysExamined" : 0.0, #索引扫描数量 这里是全表扫描,没有用索引 所以是 0 "docsExamined" : 69608.0, #浏览的文档数 这里是全表扫描 所以是整个collection中的全部文档数 "numYield" : 546.0, #该操作为了使其他操作完成而放弃的次数。通常来说,当他们需要访问 还没有完全读入内存中的数据时,操作将放弃。这使得在MongoDB为了 放弃操作进行数据读取的同时,还有数据在内存中的其他操作可以完成。 "locks" : { #锁信息,R:全局读锁;W:全局写锁;r:特定数据库的读锁;w:特定数据库的写锁 "Global" : { "acquireCount" : { "r" : NumberLong(1094) #该操作获取一个全局级锁花费的时间。 } }, "Database" : { "acquireCount" : { "r" : NumberLong(547) } }, "Collection" : { "acquireCount" : { "r" : NumberLong(547) } } }, "nreturned" : 200.0, #返回的文档数量 "responseLength" : 57695.0, #返回字节长度,如果这个数字很大,考虑值返回所需字段 "millis" : 264.0, #消耗的时间(毫秒) "planSummary" : "COLLSCAN, COLLSCAN", #执行概览 从这里看来 是全表扫描 "execStats" : { #详细的执行计划 这里先略过 后续可以用 explain来具体分析 }, "ts" : ISODate("2017-08-24T02:32:49.768+0000"), #命令执行的时间 "client" : "10.3.131.96", #访问的ip或者主机 "allUsers" : [ ], "user" : "" } ``` 常见问题: ``` 1. 如果发现 millis 值比较大,那么就需要作优化。 2. 如果docsExamined数很大,或者接近记录总数(文档数),那么可能没有用到索引查询,而是全表扫描。 3. 如果keysExamined数为0,也可能是没用索引。 4. 结合 planSummary 中的显示,上例中是 "COLLSCAN, COLLSCAN" 确认是全表扫描 5. 如果 keysExamined 值高于 nreturned 的值,说明数据库为了找到目标文档扫描了很多文档。这时可以考虑创建索引来提高效率。 6. 索引的键值选择可以根据 query 中的输出参考,上例中 filter:包含了 jzrq和jglxfldm 并且按照RsId排序,所以 我们的索引 索引可以这么建: db.f10_2_8_3_jgcc.ensureindex({jzrq:1,jglxfldm:1,RsId:1} ``` planSummary的其他类型可选项 ``` COLLSCAN #全表扫描 避免 IXSCAN #索引扫描 可以改进 选用更高效的索引 FETCH #根据索引去检索指定document SHARD_MERGE #将各个分片返回数据进行merge 尽可能避免跨分片查询 SORT #表明在内存中进行了排序(与老版本的scanAndOrder:true一致) 排序要有index LIMIT #使用limit限制返回数 要有限制 Limit+(Fetch+ixscan)最优 SKIP #使用skip进行跳过 避免不合理的skip IDHACK #针对_id进行查询 推荐,_id 默认主键,查询速度快 SHARDING_FILTER #通过mongos对分片数据进行查询 SHARDING_FILTER+ixscan最优 COUNT #利用db.coll.explain().count()之类进行count运算 COUNTSCAN #count不使用Index进行count时的stage返回 避免 这种情况建议加索引 COUNT_SCAN #count使用了Index进行count时的stage返回 推荐 SUBPLA #未使用到索引的$or查询的stage返回 避免 TEXT #使用全文索引进行查询时候的stage返回 PROJECTION #限定返回字段时候stage的返回 选择需要的数据, 推荐PROJECTION+ixscan ``` ####删掉system.profile集合 首先,通过将其级别设置为0来关闭分析,然后删掉 ``` db.setProfilingLevel(0) db.system.profile.drop() ``` ####CPU异常 导致数据库服务器CPU异常,通常的原因有以下: 1. 过多的慢查询 2. 查询里需要输出极大的数据,从而导致序列化数据时cpu占用过高 3. 高频的查询 对于第1点,可通过上述的慢查询分析,得到是哪些操作过慢,优化对应的逻辑或索引即可 对于第2、3点,可在对应的库shell中,执行命令`db.currentOp()`查看当前正在运行的命令状态,通过数据反推出是在执行何逻辑导致,执行后会得到类似下图的结果:  返回正在执行的查询操作 ``` db.currentOp({ op: 'query' }) ``` 返回正在执行的写操作 ``` db.currentOp({ op: { $in: ['update', 'insert', 'delete'] } }) ``` 返回超时的操作 ``` db.currentOp({ active: true, 'secs_running': { $gt: 2 } }) ``` 返回正在等待的操作 ``` db.currentOp({ waitingForLock: true }) ``` 标签: none
评论已关闭