MongoDB实例内存使用率高问题

云数据库 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 版将存储引擎WiredTigercachesize设置为实际申请的实例内存规格大小的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节点会维持⼀个最大约256Mbuffer用于数据回放。在创建索引方面,当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以内,连接太多会导致内存和多线程上下文的开销增加,影响请求处理延时。

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

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

  • 加速tcmalloc释放内存。如果您的数据库实例内存使用率超过80%,可以通过控制台的参数设置调整tcmalloc相关参数进行优化。优先开启tcmallocAggressiveMemoryDecommit参数,因为此参数经过丰富的实践验证,对于解决内存相关问题有显著效果。如果调整此参数后未达到预期效果,再考虑渐进式调大tcmallocReleaseRate参数值,例如参数初始值为1,先调整至3,再调整至5。

    重要

    建议在业务低峰期进行调整,因为tcmallocAggressiveMemoryDecommittcmallocReleaseRate参数调整可能会导致数据库性能退化,如果调整后对业务产生了影响请及时回滚。

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

参考

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。