human: 如何处理七牛云的云主机排查MongoDB CPU使用率高

gpt: [云主机](https://developer.qiniu.com/qvm) > 使用文档 > 排查MongoDB CPU使用率高

#  排查MongoDB CPU使用率高

最近更新时间: 2020-05-15 10:54:00

在使用云数据库MongoDB的时候您可能会遇到MongoDB
CPU使用率很高或者CPU使用率接近100%的问题，从而导致数据读写处理异常缓慢，影响正常业务。本文主要帮助您从应用的角度排查MongoDB
CPU使用率高的问题。  
  

## **分析MongoDB数据库正在执行的请求**

  1. 通过Mongo Shell连接实例。  
执行db.currentOp()命令，查看数据库当前正在执行的操作。

  2. 该命令的输出示例如下。
    
         {
     "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"
     },
     ....
             },
    

  
您需要重点关注以下几个字段。

字段 | 返回值说明  
---|---  
client | 该请求是由哪个客户端发起的。  
opid | 操作的唯一标识符。（如果有需要，可以通过db.killOp(opid)直接终止该操作。）  
secs_running | 表示该操作已经执行的时间，单位为秒。(如果该字段返回的值特别大，需要查看请求是否合理。)  
microsecs_running | 表示该操作已经执行的时间，单位为微秒。(如果该字段返回的值特别大，需要查看请求是否合理。)  
ns | 该操作目标集合。  
op | 表示操作的类型。通常是查询、插入、更新、删除中的一种。  
locks | 跟锁相关的信息。  
  
  
通过 db.currentOp()
查看正在执行的操作，分析是否有不正常耗时的请求正在执行。例如您的业务平时CPU使用率不高，运维管理人员连到MongoDB数据库执行了一些需要全表扫描的操作导致CPU使用率非常高，业务响应缓慢，此时需要重点关注执行时间非常耗时的操作。

**说明：** 如果发现有异常的请求，您可以找到该请求对应的opid，执行db.killOp(opid)终止该请求。  
---  
  
  
如果您的应用刚刚上线，MongoDB实例的CPU使用率马上处于持续很高的状态，执行db.currentOp()，在输出结果中未发现异常请求，您可参见下述小节分析数据库慢请求。

## **分析MongoDB数据库的慢请求**

云数据库MongoDB默认开启了慢请求Profiling
，系统自动地将请求时间超过100ms的执行情况记录到对应数据库下的system.profile集合里。

  1. 通过Mongo Shell连接实例。 
  2. 通过use 命令进入指定数据库。 
    
             use mongodbtest
    

  3. 执行如下命令，查看该数据下的慢请求日志。 
    
             db.system.profile.find().pretty()
    

  4. 分析慢请求日志，查找引起MongoDB CPU使用率升高的原因。  
以下为某个慢请求日志示例，可查看到该请求进行了全表扫描，扫描了11000000个文档，没有通过索引进行查询。

    
        {
         "op" : "query",
         "ns" : "123.testCollection",
         "command" : {
                 "find" : "testCollection",
                 "filter" : {
                         "name" : "zhangsan"
                 },
                 "$db" : "123"
         },
         "keysExamined" : 0,
         "docsExamined" : 11000000,
         "cursorExhausted" : true,
         "numYield" : 85977,
         "nreturned" : 0,
         "locks" : {
                 "Global" : {
                         "acquireCount" : {
                                 "r" : NumberLong(85978)
                         }
                 },
                 "Database" : {
                         "acquireCount" : {
                                 "r" : NumberLong(85978)
                         }
                 },
                 "Collection" : {
                         "acquireCount" : {
                                 "r" : NumberLong(85978)
                         }
                 }
         },
         "responseLength" : 232,
         "protocol" : "op_command",
         "millis" : 19428,
         "planSummary" : "COLLSCAN",
         "execStats" : {
                 "stage" : "COLLSCAN",
                 "filter" : {
                         "name" : {
                                 "$eq" : "zhangsan"
                         }
                 },
                 "nReturned" : 0,
                 "executionTimeMillisEstimate" : 18233,
                 "works" : 11000002,
                 "advanced" : 0,
                 "needTime" : 11000001,
                 "needYield" : 0,
                 "saveState" : 85977,
                 "restoreState" : 85977,
                 "isEOF" : 1,
                 "invalidates" : 0,
                 "direction" : "forward",
    ....in"
                 }
         ],
         "user" : "root@admin"
             }  
    

通常在慢请求日志中，您需要重点关注以下几点。  
1.全表扫描（关键字： COLLSCAN、 docsExamined ）

全集合（表）扫描COLLSCAN 。
当一个操作请求（如查询、更新、删除等）需要全表扫描时，将非常占用CPU资源。在查看慢请求日志时发现COLLSCAN关键字，很可能是这些查询占用了CPU资源.

**说明：** 如果这种请求比较频繁，建议对查询的字段建立索引的方式来优化。  
---  
  
  
通过查看docsExamined的值，可以查看到一个查询扫描了多少文档。该值越大，请求所占用的CPU开销越大。

2.不合理的索引（关键字： IXSCAN、keysExamined ）

**说明：**  
1\. 索引不是越多越好，索引过多会影响写入、更新的性能。  
2\. 如果您的应用偏向于写操作，索引可能会影响性能。  
---  
  
  
通过查看keysExamined字段，可以查看到一个使用了索引的查询，扫描了多少条索引。该值越大，CPU开销越大。  
如果索引建立的不太合理，或者是匹配的结果很多。这样即使使用索引，请求开销也不会优化很多，执行的速度也会很慢。  
如下所示，假设某个集合的数据，x字段取值的重复率很高（假设只有1、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} 的区别，可参见复合索引官方文档。  
  
3.大量数据排序（关键字： SORT、hasSortStage ）  
当查询请求里包含排序的时候， system.profile 集合里的hasSortStage字段会为 true
。如果排序无法通过索引满足，MongoDB会在查询结果中进行排序。而排序这个动作将非常消耗CPU资源，这种情况需要对经常排序的字段建立索引的方式进行优化。

**说明：** 当您在system.profile集合里发现SORT关键字时，可以考虑通过索引来优化排序。  
---  
  
  
其他还有诸如建立索引、aggregation（遍历、查询、更新、排序等动作的组合）
等操作也可能非常耗CPU资源，但本质上也是上述几种场景。更多profiling的设置请参见profiling官方文档。

以上内容是否对您有帮助？