MongoDB实例的CPU使用率是一个非常重要的监控指标。如果MongoDB实例的CPU使用率过高,会导致MongoDB响应缓慢,甚至业务不可用。本文介绍查看MongoDB实例CPU使用率的方法,以及导致CPU使用率高的原因和优化策略。
查看方法
监控图查看:在MongoDB管理控制台的监控信息页面,可以查看云数据库 MongoDB 版的CPU使用率。关于采集粒度和操作步骤的更多信息,请参见基本监控。
云数据库 MongoDB 版不同架构的节点构成不同,您可以选择对应节点,查看其CPU使⽤率。
副本集架构。包括一个主节点(Primary节点)、一个或多个从节点(Secondary节点)、一个隐藏节点(Hidden节点)和可选的一个或多个只读节点(ReadOnly节点)。
分片集群架构。各个分片(Shard)的CPU使用与副本集保持一致;ConfigServer仅存储配置元数据,基本上不会造成CPU瓶颈,一般可以忽略;Mongos路由节点的CPU使用往往与聚合结果集、并发请求数有关。
CPU使用率与实例规格有关,例如实例规格是8核16 GB,CPU使用率显示100%时,表示该实例已经用满了8核的CPU,而并非显示800%。
常见原因
扫描行数过多
MongoDB为多线程应用,如果存在单个查询扫描行数过多,该查询所在线程的CPU占用时间会变长,当请求堆积或此类查询的并发度足够高时,整个MongoDB实例的CPU占用就会过高。从某种意义上说,MongoDB的CPU使用率与该实例的总扫描行数成正相关的关系。导致查询扫描行数过多的场景有以下几个方面:
全表扫描
当您在慢日志或者 system.profile集合(需主动开启database profiler)中发现
COLLSCAN
关键字时,就表示该查询进行了全表扫描。关于查看慢日志与database profiler的更多信息,请参见慢日志。关于查询执行计划的更多解读信息,请参见:Explain Results和Cursor Methods。
不合理的索引设计与使用
当查询的
docsExamined
(文档扫描条数)关键字超过1000且执行频率较高时,需关注对应查询。除全表扫描外,其他导致docsExamined
过多的情况如下:多条件过滤时,未使用组合索引或不满足前缀匹配原则。
查询过于复杂,或者存在⼤量的聚合类操作,基本⽆法从索引层⾯做到极致优化,或者导致查询⾛错解析计划。
数据列的数据选择性和运行频率的评估错误,未能做到最好的折中。
并发过大
业务请求量过⼤,并发过⾼也会导致CPU占用高。针对此问题,如果确认查询层面没有问题,一般通过添加CPU核数的⽅式解决。
其他原因
频繁短连接。MongoDB 3.X后使用的默认的身份认证机制是SCRAM-SHA1,需要进⾏⼀些CPU密集型操作,⽐如哈希计算等。在⾼并发的短连接场景下,这些哈希计算占⽤的CPU将会被放⼤很多倍,进⽽耗尽整个机器的CPU资源。其中⼀个现象是在运⾏⽇志中可以发现⼤量包含saslStart的报错信息。阿⾥云MongoDB在PHP⾼并发短连接场景做了⼀定程度的优化,通过在内核层⾯优化改写内置的随机函数的⽅式⼤幅降低了MongoDB实例CPU使⽤率。
TTL索引导致从节点(Secondary)CPU使用率高于主节点(Primary)CPU使用率。如果遇到这种情况,建议您直接忽略。
自MongoDB 3.2开始实现了多线程复制,Oplog日志的回放并发度由参数replWriterThreadCount控制,默认为16。所以尽管Secondary节点不承载任何业务写,在部分场景下CPU使⽤率也可能会超过Primary节点。例如,在Primary节点上针对某⼀张表设置了TTL过期⾃动删除,在Primary节点上,系统会根据时间列的索引批量删除数据,效率很⾼,同时会将该操作转换成很多单条的Delete操作发送给Secondary节点;在Secondary节点上,回放Oplog日志时效率较低,所以在多线程回放的情况下容易引起该节点CPU升⾼。
排查方法
查看和Kill活跃会话
正常运行中的MongoDB实例会话突然飙升至100%,绝⼤部分情况是业务侧的变化引起,可能是由于扫描行数过多、数据排序和聚合、业务流量突增等原因导致的。您可以通过以下方法排查:
在MongoDB管理控制台的 页面查看当前执行的活跃会话。分析不符合预期执行时间的查询操作,可以选择Kill活跃会话或者其他方法解决该问题。
通过MongoDB⾃带的命令db.currentOp()查看和分析更加详细的活跃会话执行情况。如果需要,可以通过命令db.killOp()主动Kill⾮预期内的慢查询。详情请参见db.currentOp()和db.killOp()。
记录和查看日志
当CPU使用率异常上升时,可以通过慢日志或审计日志深入分析异常请求。通过检查COLLSCAN
、docsExamined
等关键字,进一步确认是否存在扫描行数过多的情况。
审计日志
在MongoDB管理控制台的 页面开通并查看审计⽇志。关于审计⽇志的开通与使用方法,请参见开通日志审计功能。
慢日志
重要您只能查看7天内的慢日志。
2021年06月06日后新购买的实例,需要先开通审计日志功能,并设置需要审计的操作类型包含admin和slow,然后查看慢日志。只能查看审计日志功能开通之后出现的慢日志。
在MongoDB管理控制台的 页面,根据业务需求设置operationProfiling.mode(慢查询的模式)和operationProfiling.slowOpThresholdMs(慢查询的阈值)。
MongoDB在profiling上共有3种设置模式:
关闭profiling,即不记录任何请求。
针对所有请求开启profiling,即将所有请求的执⾏都记录到system.profile集合。
慢查询profiling,将超过⼀定阈值的请求,记录到system.profile集合。
关于profiling相关参数含义及更多信息,请参见:Database Profiler。
在
中查看慢日志。
优化策略
优化索引
索引优化是减少MongoDB单个查询扫描行数的最优⽅案。从底层设计上,MongoDB的索引设计原理几乎与MySQL保持一致(或许种类和功能更丰富一些),所以适用于MySQL的索引优化策略基本也都适用于 MongoDB实例。
关于索引的创建与使用方式,请参见云数据库MongoDB版创建索引最佳实践,或以下官方文档:
组合索引的原理和使⽤⽅法,详情请参见:Compound Indexes。
使⽤索引排序,详情请参见:Use Indexes to Sort Query Results。
使⽤Hint固化执⾏计划,详情请参考:Cursor Methods和cursor.hint()。
索引的数据选择性和运⾏频率折中⽅法,详情请参见:Create Queries that Ensure Selectivity。
添加CPU核数
如果确认查询层⾯没有问题,CPU占用高是由于业务请求量过⼤、并发过⾼所导致,可以通过添加CPU核数的⽅式解决。⼀般有如下⽅法:
单实例垂直配置升降级,使得单实例能够承载更多的读写量。
配置副本集层面的读写分离,或者添加该副本的只读实例。
升级至MongoDB分片集群,通过数据水平拆分的方式横向,线性扩展系统性能。
如果是Mongos路由节点CPU占满,则直接添加Mongos节点个数并设置Mongos节点的负载均衡,关于Mongos节点负载均衡的说明,请参见负载均衡。
云数据库 MongoDB 版配置变更的更多信息,请参见变更单节点实例配置、变更副本集实例配置和变更分片集群实例配置。
控制表的数量和执行频率
针对全表扫描的问题,优先通过添加索引的方式优化,如果已无法通过此方式优化,建议在业务侧控制表的数据量和执行频率。
避免频繁短连接
建议您尽可能使⽤⻓连接。