云数据库 MongoDB 版的内存使用率是一个非常重要的监控指标。本文介绍查看云数据库 MongoDB 版实例内存使用率的方法,以及导致内存使用率高的原因和优化策略。
背景信息
云数据库 MongoDB 版进程启动后,不仅会加载二进制文件和依赖的各种系统库文件到内存,而且负责内存的分配和释放工作,例如客户端的连接管理、请求处理和存储引擎等。默认情况下,云数据库 MongoDB 版的内存分配器是Google tcmalloc,内存主要被Wiredtiger存储引擎和客户端连接及请求处理占用。
查看方法
监控图分析
在MongoDB管理控制台的监控信息页面,可以查看云数据库 MongoDB 版的内存使用率。云数据库 MongoDB 版不同架构的节点构成不同,您可以选择对应节点,查看其内存使用率。
副本集架构。包括一个主节点(Primary节点)、一个或多个从节点(Secondary节点)、一个隐藏节点(Hidden节点)和可选的一个或多个只读节点(ReadOnly节点)。
分片集群架构。各个分片(Shard)的内存使用与副本集架构一致,Config Server用于存储配置元数据,Mongos路由节点的内存使用率和聚合结果集、连接数大小、元数据大小相关。
命令行查看
通过在MongoDB Shell中使用
db.serverStatus().mem
命令查看和分析内存占用情况。MongoDB Shell连接方法,请参见连接MongoDB。返回示例如下:
{ "bits" : 64, "resident" : 13116, "virtual" : 20706, "supported" : true }
// resident 表示该mongod物理节点占用的物理内存大小,单位为MB。
// virtual 表示该mongod物理节点占用的虚拟内存大小,单位为MB。
关于serverStatus的更多信息,请参见serverStatus。
常见原因
引擎内存
云数据库 MongoDB 版的大部分内存都会用于存储引擎缓存。考虑到兼容性和安全性,云数据库 MongoDB 版将存储引擎WiredTiger的CacheSize设置为实际申请的实例内存规格大小的60%左右。具体规格,请参见产品规格。
如果存储引擎缓存使用了CacheSize配置大小的95%,说明实例负载已经很高,处理用户请求的线程会参与淘汰CLEAN PAGE。如果存储引擎脏数据缓存占比超过20%,用户线程会参与淘汰DIRTY PAGE。在此过程中,用户侧会明显感觉到请求存在阻塞。具体规则,请参见 eviction参数说明。
您可以使用以下方法查看引擎内存的使用情况:
查看WiredTiger引擎的内存占用大小
在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"), ...... }
连接和请求占用的内存
如果实例的连接数很高,可能会消耗一部分的内存,原因如下:
线程栈开销。每个连接,后端都有对应处理这个连接上的请求的线程,每个线程最多可以开销1MB的线程栈,通常情况下在几十~几百KB。
TCP连接内核缓冲区。每个TCP连接在内核层面有读缓冲区和写缓冲区,由TCP内核参数tcp_rmem和tcp_wmem等确定,这块的内存使用用户无需关心。但并发连接越多,默认套接字缓存越大,则TCP占用内存越大。
tcmalloc内存管理。每接收到一个请求,会创建请求上下文并分配临时缓冲区(如请求包、应答包和排序的临时缓冲区等)。请求结束后,临时缓冲区会释放归还给内存分配器 tcmalloc,tcmalloc优先归还到自己的缓存里,再逐步归还至操作系统。很多情况下,内存使用率高的原因是tcmalloc未及时归还内存至操作系统,最多可达到几十GB。
您可以使用以下方法排查:
查看连接使用情况
在MongoDB管理控制台的监控信息页面,可以查看云数据库 MongoDB 版的连接使用情况。
通过Mongo Shell查询连接数。具体方法,请参见如何查询及限制连接数。
查看tcmalloc未归还操作系统的内存大小
通过
db.serverStatus().tcmalloc
命令查看tcmalloc未归还操作系统的内存大小。其中tcmalloc cache=pageheap_free_bytes+total_free_byte。返回示例如下:{ ...... "tcmalloc":{ "pageheap_free_bytes":NumberLong("3048677376"), "pageheap_unmapped_bytes":NumberLong("544994184"), "current_total_thread_cache_bytes":95717224, "total_free_byte":NumberLong(1318185960), ...... } }
说明tcmalloc的更多信息,请参见tcmalloc。
元数据信息占用的内存
云数据库 MongoDB 版的数据库、集合、索引等内存元数据,数量很多时将占用较多的内存。低版本的云数据库 MongoDB 版,可能存在以下问题:
云数据库MongoDB 4.0以下版本,全量逻辑备份期间可能打开非常多的文件句柄,如果未及时归还操作系统,将导致内存快速上涨。
云数据库MongoDB 4.0及以下版本在大量删除集合后,可能未能删除文件句柄,将导致内存泄漏。
创建索引过程中的内存消耗
正常数据写入时,Secondary节点会维持一个最大约256M的buffer用于数据回放。在创建索引方面,Secondary节点回放过程中可能消耗更多的内存。
云数据库MongoDB 4.2以下版本,创建索引支持
background
选项。当指定{background:true}
时,进行后台创建,回放创建索引是串行的,最多可能消耗500M内存。云数据库MongoDB 4.2及以上版本默认废弃了
background
选项。允许Secondary节点并行回放创建索引,会消耗更多的内存,多个索引同时创建时可能导致实例内存溢出。
创建索引期间可能造成的内存消耗,详情请参见index-build-impact-on-database-performance和index-build-process。
PlanCache内存占用
在某些场景下,一个请求可能存在的执行计划非常多,这时PlanCache会消耗比较多的内存。
查看PlanCache内存占用大小:云数据库MongoDB 4.0及以上版本,可以通过db.serverStatus().metrics.query.planCacheTotalSizeEstimateBytes
命令查看。
如果您的实例为云数据库MongoDB 4.0版本,但执行上述命令时没有相关字段,说明实例小版本太低,建议升级小版本。具体操作,请参见升级数据库小版本。
PlanCache内存占用的更多信息,请参见Secondary node memory arise while balancer doing work。
优化策略
内存优化并不是尽可能地减少内存使用,而是在保证系统性能正常的前提下,内存足够使用且稳定,在机器资源和性能中达到一个最佳的折衷。
云数据库 MongoDB 版帮助用户指定了CacheSize的大小,该值不支持修改。您可以使用以下策略进行内存优化:
控制并发连接数。根据性能测试结果,数据库中能够创建100个长连接,默认MongoDB Driver可以和后端建立100个连接池。当存在很多客户端时,就需要降低每个客户端的连接池大小,一般建议与整个数据库建立的长连接控制在1000以内,连接太多会导致内存和多线程上下文的开销增加,影响请求处理延时。
降低单次请求的内存开销。例如通过创建索引减少集合的扫描、内存排序等方法,优化查询性能。
升级内存配置。在连接数合适的情况下,优化查询后内存占用持续增高,建议升级内存配置,避免可能存在内存溢出(OOM,Out of Memory)和大量清除缓存而导致实例可用性受到影响。
加速tcmalloc释放内存。如果您的数据库实例内存使用率超过80%,可以在控制台的参数设置页面调整tcmalloc相关参数。
优先开启tcmallocAggressiveMemoryDecommit参数。因为此参数经过丰富的实践验证,对于解决内存相关问题有显著效果。
渐进式调大tcmallocReleaseRate参数值。如果调整上述参数后未达预期,再渐进式调大tcmallocReleaseRate参数值,例如从1调整到3,再到5。
重要建议在业务低峰期调整。tcmallocAggressiveMemoryDecommit和tcmallocReleaseRate参数调整可能会导致数据库性能退化,业务受到影响后请及时回滚。
优化库表数量。如果数据库实例存在过多的库表,可以移除不需要的集合、索引,整合多表数据,拆分实例,或者迁移到分片集群等。具体操作,请参见库表数太多导致实例卡顿或异常。
如果您在使用云数据库 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。 |
eviction_updates_target | 2.5% | 当cache update ratio超过eviction_updates_target,后台evict线程开始淘汰小对象相关的内存碎片空间。 |
eviction_updates_trigger | 10% | 当cache update ratio超过eviction_updates_trigger,用户线程也开始淘汰小对象相关的内存碎片空间。 |
常见问题
Q:MongoDB如何增加聚合操作的内存限制?
A:云数据库 MongoDB 版暂不支持直接添加聚合操作的内存限制。MongoDB的聚合操作每个阶段都有100 MB的内存限制,如果某个阶段超过了该限制,系统会产生报错,您可以在聚合语句中显示指定{allowDiskUse:true}
的选项解决该问题。从MongoDB 6.0版本开始,MongoDB支持了全局默认参数allowDiskUseByDefault,当出现需要消耗过多内存的聚合操作时,MongoDB会临时使用部分磁盘空间来避免过多的内存占用。其他降低内存使用的策略,请参见优化策略。