【巡检问题分析与最佳实践】MongoDB 内存高问题

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: 本文将由浅入深帮您查看、分析和优化云数据库MongoDB的内存使用。

往期分享

RDS MySQL

RDS MySQL 实例空间问题

RDS MySQL 内存使用问题

RDS MySQL 活跃线程数高问题

RDS MySQL 慢SQL问题

RDS MySQL 实例IO高问题

RDS MySQL 小版本升级最佳实践

RDS PostgreSQL

RDS PostgreSQL 实例IO高问题

RDS PostgreSQL 慢SQL问题

RDS PostgreSQL CPU高问题

RDS SQL Server

RDS SQL Server 磁盘IO吞吐高问题

RDS SQL Server CPU高问题

RDS SQL Server 空间使用问题

Redis

Redis 流控问题

Redis 内存高问题

Redis CPU高问题

概述

阿里云数据库MongoDB的内存使用率是一个非常重要的监控指标,然而MongoDB的内存使用率并非是简单的越小越好,而突发的高内存使用率也需要引起用户足够的关注,因为它往往是业务侧的变动引起,突发的内存飙升容易引起实例OOM。

MongoDB 进程启动后,除了跟普通进程一样,加载 binary、依赖的各种library 到内存,其作为一个DBMS,还需要负责客户端连接管理、请求处理、数据库元数据、存储引擎等很多工作,这些工作都涉及内存的分配与释放。默认情况下,MongoDB使用Google tcmalloc作为内存分配器,内存占用的大头主要是"Wiredtiger存储引擎"与 "客户端连接及请求的处理"。

本文将由浅入深帮您查看、分析和优化云数据库MongoDB的内存使用。

查看内存使用

部署架构为副本集模式下,提供有多种查看内存使用的方法,您可以根据自身需求,由浅入深了解MongoDB的内存使用情况。

部署架构为分片集群模式下,各个Shard的内存使用与副本集保持一致;Config Server仅仅存储配置元数据,基本上不会造成内存瓶颈,一般可以忽略;Mongos路由节点的内存使用往往与聚合结果集,连接数大小,元数据大小有关。

监控图分析

MongoDB副本集由多种角色组成,一个角色可能对应一个或多个物理节点。阿里云MongoDB对用户暴露Primary和Secondary节点,另外还提供有只读实例的角色。可以通过点击"监控信息",选择对应的角色查看MongoDB内存有关的监控情况,如下图:

1.png

除了总体内存使用率外,MongoDB的WiredTiger引擎的内存使用也至关重要,下图展示了WiredTiger引擎的内存使用情况,其中"maximum bytes configured"即当前配置的引擎cache的总大小,与配置文件中的cacheSizeGB保持一致;"bytes_read_into_cache"表示每秒从磁盘将数据加载到内存的大小 ,"bytes_written_from_cache"表示每秒从cache中刷新脏页到磁盘的大小,这两个值越大,反应内存使用越吃紧,磁盘压力相应也会越大。

2.png

命令行查看

除了使用阿里云控制台提供的监控图查看以外,您也可以直接使用MongoDB Shell命令查看和分析内存占用情况,如下示例:

PRIMARY> db.serverStatus().mem
{ "bits" : 64, "resident" : 13116, "virtual" : 20706, "supported" : true }
//resident表示该mongod物理节点占用的物理内存大小,单位为M
//virtual表示该mongod物理节点占用的虚拟内存大小,单位为M

另外,MongoDB serverStatus中自带了大量的关于wiredTiger引擎和tcmalloc的内存使用使用详情,可以通过的db.serverStatus().wiredTiger.cache和db.serverStatus().tcmalloc进行查看,后文我们会重点展开讲解。

更多serverStatus的信息展示详情建议参考:https://docs.mongodb.com/manual/reference/command/serverStatus/

内存使用详细分析

引擎内存使用

MongoDB使用tcmalloc作为内存分配器,其中WiredTiger引擎占用的Cache是其中最大的一部分,引擎内存最大使用内存由配置参数cachesize决定。为了兼容性能和安全性,阿里云数据库MongoDB为cachesize设置的大小默认为申请内存的60%左右,由于存在向上取整等因素,详细的cachesize大小设置可参照下表:

class_code 规格大小 实际大小 Wt cacheSizeGB
dds.mongo.small 1024 2048 1
dds.mongo.mid 2048 4096 1
dds.mongo.standard 4096 7168 2
dds.mongo.large 8192 12288 5
dds.mongo.xlarge 16384 24576 10
dds.mongo.2xlarge 32768 49152 20
dds.mongo.4xlarge 65536 98304 40
dds.mongo.monopolize 225280 225280 96
dds.mongo.2xmonopolize 450560 450560 264
mongo.x8.medium 16384 16384 10
mongo.x8.large 32768 32768 20
mongo.x8.xlarge 65536 65536 40
mongo.x8.2xlarge 131072 131072 77
mongo.x8.4xlarge 262144 262144 154
dds.sn4.8xlarge.3 131072 131072 64

通常情况下,即使数据量远超过cachesize的大小,wiredTiger也不会将cachesize耗尽,如果超过了cachesize配置大小的95%,那表示系统性能已经处于较为危险的状态。WiredTiger 在内存使用接近一定阈值就会开始做淘汰,避免内存使用满了阻塞用户请求,这个过程称为"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 实例,cache used 一般会在 0.8 * cacheSizeGB 及以下,偶尔超出问题不大;如果出现 used>=95% 或者 dirty>=20%,并一直持续,说明内存淘汰压力很大,用户的请求线程会阻塞参与page淘汰,请求延时就会增加,这时可以考虑"扩大内存"或者"扩大IOPS"。

查看当前wiredTigerd引擎占用的内存大小

3.png

查看当前wiredTiger引擎的cache dirty比例

您可以通过mongostat或者阿里云数据库自治服务DAS实时查看当前的cache dirty,目前阿里云数据库MongoDB暂不支持cache dirty历史情况查看。

4.png

更多mongostat的使用方式可以参考:https://docs.mongodb.com/v4.2/reference/program/mongostat/

连接和请求占用的内存

如果实例的连接数很大,可能会消耗相当一部分的内存,这是因为:

  • 每个连接,后端启动一个线程处理这个连接上的请求,每个线程最多1MB的线程栈开销,平时一般在几十KB - 几百KB 之间。
  • 每个tcp连接在内核层面有read、write buffer,极端情况下可能涨到16MB,由tcp内核参数tcp_rmem和tcp_wmem等确定,这块的内存使用用户无需关心。但并发连接越多,默认套接字缓存越大,则tcp占用内存越大。
  • 每接收到一个请求,会有个请求上下文,整个过程中可能分配很多临时buffer,比如请求包、应答包、从引擎数据的buffer、排序的临时buffer等,这些在请求结束都会释放,但这个释放只是说归还给内存分配器 tcmalloc,tcmalloc优先会还到自己的cache里;然后逐步再归还给操作系统。所以很多情况下,内存使用率高的原因是tcmalloc未及时归还内存至操作系统,这一块最大可能达到数十GB。

关于tcmalloc未归还OS的内存大小,可以通过以下命令查看:

tcmalloc cache大小=pageheap_free_bytes + total_free_byte

5.png

关于更多mongodb tcmalloc的更多内容参考:https://mongoing.com/archives/34751

元数据信息占用的内存

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

阿里云MongoDB建议库表数量控制在10W以内,并使用MongoDB4.0以上的内核版本,更多关于这块的详细分析参考:

https://jira.mongodb.org/browse/WT-4336

创建index过程中的内存消耗

   正常的业务数据写入情况下,Secondary会维持一个最大约256M的buffer用于数据回放。在Create Index方面,当Primary创建index完成后,Secondary节点回放过程中可能消耗更多的内存。在MongoDB4.2以前,在Primary上通过非background的方式create index,后端回放创建index是串行的,最多可能消耗500M内存;而MongoDB4.2以后默认废弃了background选项,允许Secondary并行回放create index,那就会消耗更多的内存,多个index同时build时可能导致实例OOM。

更多关于create index期间可能造成的内存消耗参考:

https://docs.mongodb.com/manual/core/index-creation/#index-build-impact-on-database-performance

https://docs.mongodb.com/manual/core/index-creation/#index-build-process

PlanCache内存占用

    在某些场景下,比如一个SQL可能存在的执行计划非常多,这时plancache可能会消耗比较多的内存,在高版本MongoDB中可以通过以下命令查看PlanCache占用的内存大小,默认大小未Byte。

mgset-xxx:PRIMARY> db.serverStatus().metrics.query.planCacheTotalSizeEstimateBytes

NumberLong(750695)

更多内容参考我们给官方提的bug链接:https://jira.mongodb.org/browse/SERVER-48400

内存使用的通用优化思路

首先需要强调一点,内存优化并非是为了尽可能减少内存使用,而是在保证系统性能完全没问题的前提下,内存使用尽可能稳定和够用,从而在机器资源和性能中达到一个最佳的折衷。

阿里云MongoDB帮用户指定了比较合适的CacheSize大小,不建议也无法修改该值。

控制并发连接数,这个是最直接有效的方法,根据性能测试结果,100个长连接足以压满数据库,默认 MongoDB driver也是跟后端建立100的连接池。当连接到客户端(ECS机器)很多时,就需要降低每个客户端的连接池大小,一般建议跟整个数据库建立的长连接控制在1000以内,连接太多,一是内存开销,另外多线程上下文的开销也会增加,影响请求处理延时。

降低单次请求的内存开销,比如通过建索引,减少 COLLSCAN、内存排序等。

另外,在连接数合适的情况下内存占用持续走高,建议升级内存配置,从而避免可能存在OOM和疯狂的evict导致系统性能急剧下滑。

最后,如果您在使用阿里云MongoDB过程中遇到更多可能存在内存泄漏的场景,可以与技术支持人员联系。





相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。   相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
相关文章
|
1月前
|
缓存 Java
Java中循环创建String对象的内存管理分析
Java中循环创建String对象的内存管理分析
25 2
|
1天前
|
缓存 监控 NoSQL
【MongoDB 专栏】MongoDB 的内存管理与优化
【5月更文挑战第11天】MongoDB的内存管理优化对性能至关重要,涉及数据缓存、索引及执行操作的内存使用。动态内存管理根据访问模式和负载调整,可通过配置参数优化,如设置合适缓存大小,调整内存分配参数。索引管理也很重要,需定期评估优化,避免内存占用过高。监控内存使用、数据清理压缩、架构规划也是优化手段。面对挑战,如高并发下的内存不足,需灵活调整策略,平衡系统资源。不断学习新方法,提升内存管理能力,以优化MongoDB性能。
【MongoDB 专栏】MongoDB 的内存管理与优化
|
2天前
|
存储 监控 NoSQL
【MongoDB 专栏】MongoDB 分片策略与最佳实践
【5月更文挑战第10天】MongoDB 分片是应对大数据量的扩展策略,涉及哈希和范围分片两种策略。分片架构包含分片服务器、配置服务器和路由服务器。最佳实践包括选择合适分片键、监控调整、避免热点数据等。注意数据分布不均和跨分片查询的挑战。通过实例展示了如何在电商场景中应用分片。文章旨在帮助理解并优化 MongoDB 分片使用。
【MongoDB 专栏】MongoDB 分片策略与最佳实践
|
5天前
|
存储 Arthas 监控
JVM工作原理与实战(三十):堆内存状况的对比分析
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了堆内存状况的对比分析、产生内存溢出的原因等内容。
11 0
|
7天前
|
缓存 Linux
linux性能分析之内存分析(free,vmstat,top,ps,pmap等工具使用介绍)
这些工具可以帮助你监视系统的内存使用情况、识别内存泄漏、找到高内存消耗的进程等。根据具体的问题和需求,你可以选择使用其中一个或多个工具来进行内存性能分析。注意,内存分析通常需要综合考虑多个指标和工具的输出,以便更好地理解系统的行为并采取相应的优化措施。
27 6
|
10天前
|
机器学习/深度学习 分布式计算 数据处理
Spark是一个基于内存的通用数据处理引擎,可以进行大规模数据处理和分析
【5月更文挑战第2天】Spark是一个基于内存的通用数据处理引擎,可以进行大规模数据处理和分析
23 3
|
12天前
|
监控 算法 测试技术
【Go语言专栏】Go语言的性能优化与内存分析
【4月更文挑战第30天】本文探讨了Go语言的性能优化策略和内存分析方法。性能优化原则包括基准测试、分析瓶颈、避免过早优化和持续监控。优化策略涉及减少内存分配、避免内存逃逸、利用并发、优化算法和数据结构以及减少系统调用。内存分析借助于Go的`pprof`工具、内存分配跟踪和第三方工具,以发现内存泄漏和管理问题。通过这些方法,开发者能提升Go程序效率和资源利用率。
|
14天前
|
缓存 Java Android开发
安卓开发中的内存泄漏分析与优化策略
【4月更文挑战第27天】 在移动应用开发领域,性能优化始终是提升用户体验的关键因素之一。特别是对于安卓平台,由于设备的硬件配置差异较大,良好的内存管理对于保证应用流畅运行尤为重要。本文将深入探讨安卓开发中常见的内存泄漏问题,并提供一系列检测和解决内存泄漏的实用策略。通过对工具的使用、代码实践以及系统架构设计的多维度分析,旨在帮助开发者有效避免和处理内存泄漏,确保应用性能稳定。
|
18天前
|
监控 NoSQL 测试技术
MongoDB性能最佳实践:如何制定更有效的基准测试?
感谢你与我们一起走过这段MongoDB性能最佳实践之旅,希望你能从中获取一些有用的信息
1594 2
|
19天前
|
Java
【Java基础】面向对象和内存分析
【Java基础】面向对象和内存分析
16 0