MongoDB CPU利用率很高,怎么破(转)

常常有用户咨询:MongoDB CPU 利用率很高,都快跑满了,应该怎么办?数据库

遇到这个问题,99.9999% 的可能性是「用户使用上不合理致使」,本文主要介绍从应用的角度如何排查 MongoDB CPU 利用率高的问题。app

Step1: 分析数据库正在执行的请求运维

用户能够经过 Mongo Shell 链接,并执行 db.currentOp() 命令,能看到数据库当前正在执行的操做,以下是该命令的一个输出示例,标识一个正在执行的操做。重点关注几个字段:测试

  • client:请求是由哪一个客户端发起的?
  • opid:操做的opid,有须要的话,能够经过 db.killOp(opid) 直接干掉的操做;
  • secs_running/microsecs_running: 这个值重点关注,表明请求运行的时间,若是这个值特别大,就得注意了,看看请求是否合理;
  • query/ns: 这个能看出是对哪一个集合正在执行什么操做;
  • lock*:还有一些跟锁相关的参数,须要了解能够看官网文档,本文不作详细介绍。
{
        "desc" : "conn632530",
        "threadId" : "140298196924160",
        "connectionId" : 632530,
        "client" : "11.192.159.236:57052",
        "active" : true,
        "opid" : 1008837885,
        "secs_running" : 0,
        "microsecs_running" : NumberLong(70),
        "op" : "update",
        "ns" : "mygame.players",
        "query" : {
            "uid" : NumberLong(31577677)
        },
        "numYields" : 0,
        "locks" : {
            "Global" : "w",
            "Database" : "w",
            "Collection" : "w"
        },
        ....
},

这里先要明确一下,经过 db.currentOp() 查看正在执行的操做,目的究竟是什么?优化

并非说咱们要将正在执行的操做都列出来,而后经过 killOp 逐个干掉;这一步的目的是要看一下,是否有「意料以外」的耗时请求正在执行。ui

好比你的业务平时 CPU 利用率不高,运维管理人员连到数据库执行了一些须要全表扫描的操做,而后忽然 CPU 利用率飙高,致使你的业务响应很慢,那么就要重点关注下那些执行时间很长的操做。spa

一旦找到罪魁祸首,拿到对应请求的 opid,执行 db.killOp(opid) 将对应的请求干掉。日志

若是你的应用一上线,cpu利用率就很高,并且一直持续,经过 db.currentOp 的结果也没发现什么异常请求,能够进入到 Step2 进行更深刻的分析。code

Step2:分析数据库慢请求排序

MongoDB 支持 profiling 功能,将请求的执行状况记录到同DB下的 system.profile 集合里,profiling 有3种模式:

  • 关闭 profiling;
  • 针对全部请求开启 profiling,将全部请求的执行都记录到 system.profile 集合;
  • 针对慢请求 profiling,将超过必定阈值的请求,记录到system.profile 集合。

默认请求下,MongoDB 的 profiling 功能是关闭,生产环境建议开启,慢请求阈值可根据须要定制,如不肯定,直接使用默认值100ms。

operationProfiling:
  mode: slowOp
  slowOpThresholdMs: 100

基于上述配置,MongoDB 会将超过 100ms 的请求记录到对应DB 的 system.profile 集合里,system.profile 默认是一个最多占用 1MB 空间的 capped collection。

查看最近3条 慢请求,{$natrual: -1} 表明按插入数序逆序

db.system.profile.find().sort({$natrual: -1}).limit(3)

在开启了慢请求 profiling 的状况下(MongoDB 云数据库是默认开启慢请求 profiling的),咱们对慢请求的内容进行分析,来找出可优化的点,常见的包括。

CPU杀手1:全表扫描

全集合(表)扫描 COLLSCAN,当一个查询(或更新、删除)请求须要全表扫描时,是很是耗CPU资源的,因此当你在 system.profile 集合 或者 日志文件发现 COLLSCAN 关键字时,就得注意了,极可能就是这些查询吃掉了你的 CPU 资源;确认一下,若是这种请求比较频繁,最好是针对查询的字段创建索引来优化。

一个查询扫描了多少文档,可查看 system.profile 里的 docsExamined 的值,该值越大,请求CPU开销越大。

> 关键字:COLLSCAN、 docsExamined

CPU杀手2:不合理的索引

有的时候,请求即便查询走了索引,执行也很慢,一般是由于合理创建不太合理(或者是匹配的结果自己就不少,这样即便走索引,请求开销也不会优化不少)。

以下所示,假设某个集合的数据,x字段的取值不多(假设只有一、2),而y字段的取值很丰富。

{ x: 1, y: 1 }
{ x: 1, y: 2 }
{ x: 1, y: 3 }
......
{ x: 1, y: 100000} 
{ x: 2, y: 1 }
{ x: 2, y: 2 }
{ x: 2, y: 3 }
......
{ x: 1, y: 100000}

要服务 {x: 1: y: 2} 这样的查询

db.createIndex( {x: 1} )         效果很差,由于x相同取值太多;
db.createIndex( {x: 1, y: 1} )   效果很差,由于x相同取值太多;
db.createIndex( {y: 1 } )        效果好,由于y相同取值不多;
db.createIndex( {y: 1, x: 1 } )  效果好,由于y相同取值少;

至于{y: 1} 与 {y: 1, x: 1} 的区别,可参考MongoDB索引原理 及 复合索引官方文档 自行理解。

一个走索引的查询,扫描了多少条索引,可查看 system.profile 里的 keysExamined 字段,该值越大,CPU 开销越大。

>关键字:IXSCAN、keysExamined

CPU杀手3:大量数据排序

当查询请求里包含排序的时候,若是排序没法经过索引知足,MongoDB 会在内存李结果进行排序,而排序这个动做自己是很是耗 CPU 资源的,优化的方法仍然是创建索引,对常常须要排序的字段,创建索引。

当你在 system.profile 集合 或者 日志文件发现 SORT 关键字时,就能够考虑经过索引来优化排序。当请求包含排序阶段时, system.profile 里的 hasSortStage 字段会为 true。

> 关键字:SORT、hasSortStage

其余还有诸如建索引,aggregationv等操做也可能很是耗 CPU 资源,但本质上也是上述几种场景;建索引须要全表扫描,而vaggeregation 也是遍历、查询、更新、排序等动做的组合。

Step3: 服务能力评估

通过上述2步,你发现整个数据库的查询很是合理,全部的请求都是高效的走了索引,基本没有优化的空间了,那么极可能是你机器的服务能力已经达到上限了,应该升级配置了(或者经过 sharding 扩展)。

固然最好的状况时,提早对 MongoDB 进行测试,了解在你的场景下,对应的服务能力上限,以便及时扩容、升级,而不是到 CPU 资源用满,业务已经彻底撑不住的时候才去作评估。

 

来源:https://www.ywnds.com/?p=9010

文章来源:张友东的博客

相关文章
相关标签/搜索