MongoDB实例内存使用率高问题

更新时间: 2023-07-13 18:18:04

云数据库MongoDB实例的内存使用率是一个非常重要的监控指标。本文介绍查看云数据库MongoDB实例内存使用率的方法,以及导致内存使用率高的原因和优化策略。

背景信息

云数据库MongoDB进程启动后,不仅会加载二进制文件和依赖的各种系统库文件到内存,而且负责内存的分配和释放工作,例如客户端的连接管理、请求处理和存储引擎等。默认情况下,云数据库MongoDB的内存分配器是Google tcmalloc,内存主要被“Wiredtiger存储引擎”和“客户端连接及请求处理”占用。

查看方法

分片集群架构下,各个分片(Shard)的内存使用与副本集架构保持一致,Config Server用于存储配置元数据,Mongos路由节点的内存使用率和聚合结果集、连接数大小、元数据大小相关。

副本集架构下,您可以通过以下方法查看内存的使用情况:

  • 监控图分析

    云数据库MongoDB副本集由多种角色组成,一个角色可能对应一个或多个物理节点。云数据库MongoDB副本集实例提供一个可供读写访问的Primary节点(主节点)、一个或多个提供高可用的Secondary节点(从节点)、一个隐藏的Hidden节点(隐藏节点)和一个或多个可选的ReadOnly节点(只读节点)。

    MongoDB管理控制台监控信息页面,可以查看云数据库MongoDB的内存使用率。

  • 命令行查看

    在MongoDB Shell中使用db.serverStatus().mem命令查看和分析内存占用情况,返回示例如下:

    { "bits" : 64, "resident" : 13116, "virtual" : 20706, "supported" : true }
    //resident 表示该mongod物理节点占⽤的物理内存大小,单位为MB。
    //virtual 表示该mongod物理节点占⽤的虚拟内存大小,单位为MB。
    说明

    serverStatus的更多信息,请参见serverStatus

常见原因

引擎内存

云数据库MongoDB的大部分内存都会用于存储引擎缓存。考虑到兼容性和安全性,云数据库MongoDB将存储引擎WiredTiger的cachesize设置为实际申请的实例内存规格大小的60%左右。具体规格,请参见实例规格参考

如果存储引擎缓存使用了cachesize配置大小的95%,说明实例负载已经很高了。出于保护自身的目的,处理用户请求的线程会主动参与到刷脏的工作中来,用户侧会明显感觉到请求存在阻塞。具体规则,请参见eviction参数说明

您可以使用以下方法查看引擎内存的使用情况:

  • 在MongoDB Shell中通过db.serverStatus().wiredTiger.cache查看。返回信息中bytes currently in the cache后的值为内存大小。返回信息示例如下:

    {
       ......
       "bytes belonging to page images in the cache":6511653424,
       "bytes belonging to the cache overflow table in the cache":65289,
       "bytes currently in the cache":8563140208,
       "bytes dirty in the cache cumulative":NumberLong("369249096605399"),
       ......
    }
  • DAS控制台性能趋势页面实时查看当前WiredTiger引擎的cache dirty比例。如何查看,请参见性能趋势

  • 通过云数据库MongoDB自带的mongostat工具查看当前WiredTiger引擎的cache dirty比例。更多信息,请参见mongostat

连接和请求占⽤的内存

如果实例的连接数很⼤,可能会消耗⼀部分的内存,原因如下:

  • 每个连接,后端都有对应处理这个连接上的请求的线程。每个线程最多可以开销1MB的线程栈,通常情况下在几十KB~几百KB。

  • 每个TCP连接在内核层⾯有读缓冲区和写缓冲区,由TCP内核参数tcp_rmemtcp_wmem等确定,这块的内存使⽤⽤户⽆需关⼼。但并发连接越多,默认套接字缓存越⼤,则TCP占⽤内存越⼤。

  • 每接收到⼀个请求,会有个请求上下⽂,整个过程中可能分配很多临时缓冲区,⽐如请求包、应答包和排序的临时缓冲区等,这些在请求结束时都会释放,但这个释放只是归还给内存分配器 tcmalloc,tcmalloc优先会还到⾃⼰的cache⾥,然后逐步再归还给操作系统。

    很多情况下,内存使⽤率⾼的原因是tcmalloc未及时归还内存⾄操作系统,这⼀块最⼤可能达到几十GB。关于tcmalloc未归还OS的内存大小,可以通过命令db.serverStatus().tcmalloc查看。其中tcmalloc cache=pageheap_free_bytes+total_free_byte。返回信息示例如下:

    {
       "generic":{
               "current_allocated_bytes":NumberLong("9641570544"),
               "heap_size":NumberLong("19458379776")
       },
       "tcmalloc":{
               "pageheap_free_bytes":NumberLong("3048677376"),
               "pageheap_unmapped_bytes":NumberLong("544994184"),
               "current_total_thread_cache_bytes":95717224,
               "total_free_byte":NumberLong(1318185960),
    ......
       }
    }
    说明

    mongodb tcmalloc的更多信息,请参见tcmalloc

元数据信息占⽤的内存

云数据库MongoDB的数据库、集合、索引等内存元数据等,如果集合和索引数量很多,这⼀块占用的内存也不容忽视。尤其在云数据库MongoDB 4.0以前的版本,全量逻辑备份期间可能打开⾮常多的⽂件句柄并且未能及时归还OS导致内存快速上涨,或者低版本的云数据库MongoDB在⼤量删除集合后可能未能删除文件句柄导致内存泄漏。

创建索引过程中的内存消耗

正常的业务数据写⼊情况下,Secondary节点会维持⼀个最⼤约256M的buffer⽤于数据回放。在创建索引方面,当Primary节点创建索引完成后,Secondary节点回放过程中可能消耗更多的内存。在云数据库MongoDB 4.2以前,在Primary节点上通过⾮background的⽅式创建索引,后端回放创建索引是串行的,最多可能消耗500M内存,而云数据库MongoDB 4.2以后默认废弃了background选项,允许Secondary节点并行回放创建索引,那就会消耗更多的内存,多个索引同时创建时可能导致实例内存溢出。

说明

创建索引期间可能造成的内存消耗,详情请参见index-build-impact-on-database-performanceindex-build-process

PlanCache内存占⽤

在某些场景下,⼀个请求可能存在的执⾏计划⾮常多,这时plancache会消耗⽐较多的内存。在⾼版本云数据库MongoDB中,您可以使用mgset-xxx:PRIMARY> db.serverStatus().metrics.query.planCacheTotalSizeEstimateBytes命令查看PlanCache占⽤的内存大小。更多信息,请参见Secondary node memory arise while balancer doing work

解决策略

内存优化并不是尽可能的减少内存使用,而是在保证系统性能正常的前提下,内存足够使用且稳定,在机器资源和性能中达到⼀个最佳的折衷。云数据库MongoDB帮助⽤户指定了CacheSize的大小,该值不支持修改。解决内存使用的策略如下:

  • 控制并发连接数。根据性能测试结果,数据库中能够创建100个长连接,默认MongoDB Driver可以和后端建⽴100个连接池。当存在很多客户端时,就需要降低每个客户端的连接池大小,⼀般建议与整个数据库建⽴的长连接控制在1000以内,连接太多会导致内存和多线程上下文的开销增加,影响请求处理延时。

  • 降低单次请求的内存开销,例如通过创建索引减少集合的扫描、内存排序等。

  • 在连接数合适的情况下内存占⽤持续增⾼,建议升级内存配置,避免可能存在内存溢出和大量清除缓存而导致系统性能急剧下滑。

  • 如果您在使⽤云数据库MongoDB过程中遇到更多可能存在内存泄漏的场景,可以联系阿里云技术⽀持处理。

参考

表 1. 实例规格参考

实例规格

规格大小

实际大小

WiredTiger的cachesize大小

dds.mongo.small

1024 MB

2048 MB

1 GB

dds.mongo.mid

2048 MB

4096 MB

1 GB

dds.mongo.standard

4096 MB

7168 MB

2 GB

dds.mongo.large

8192 MB

12288 MB

5 GB

dds.mongo.xlarge

16384 MB

24576 MB

10 GB

dds.mongo.2xlarge

32768 MB

49152 MB

20 GB

dds.mongo.4xlarge

65536 MB

98304 MB

40 GB

dds.mongo.monopolize

450560 MB

450560 MB

264 GB

mongo.x8.medium

16384 MB

16384 MB

10 GB

mongo.x8.large

32768 MB

32768 MB

20 GB

mongo.x8.xlarge

65536 MB

65536 MB

40 GB

mongo.x8.2xlarge

131072 MB

131072 MB

77 GB

mongo.x8.4xlarge

262144 MB

262144 MB

154 GB

dds.sn4.8xlarge.3

131072 MB

131072 MB

64 GB

表 2. eviction参数说明

参数

默认值

含义

eviction_target

80

当cache used超过eviction_target,后台evict线程开始淘汰CLEAN PAGE。

eviction_trigger

95

当cache used超过eviction_trigger,⽤户线程也开始淘汰CLEAN PAGE。

eviction_dirty_target

5

当cache dirty超过eviction_dirty_target,后台evict线程开始淘汰DIRTY PAGE。

eviction_dirty_trigger

20

当cache dirty超过eviction_dirty_trigger,⽤户线程也开始淘汰DIRTY PAGE。

阿里云首页 云数据库 MongoDB 相关技术圈