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

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 Tair(兼容Redis),内存型 2GB
简介: 本文将由浅入深帮您查看、分析和优化云数据库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月前
|
Web App开发 监控 JavaScript
监控和分析 JavaScript 内存使用情况
【10月更文挑战第30天】通过使用上述的浏览器开发者工具、性能分析工具和内存泄漏检测工具,可以有效地监控和分析JavaScript内存使用情况,及时发现和解决内存泄漏、过度内存消耗等问题,从而提高JavaScript应用程序的性能和稳定性。在实际开发中,可以根据具体的需求和场景选择合适的工具和方法来进行内存监控和分析。
|
2月前
|
编译器 C语言
动态内存分配与管理详解(附加笔试题分析)(上)
动态内存分配与管理详解(附加笔试题分析)
56 1
|
3月前
|
程序员 编译器 C++
【C++核心】C++内存分区模型分析
这篇文章详细解释了C++程序执行时内存的四个区域:代码区、全局区、栈区和堆区,以及如何在这些区域中分配和释放内存。
56 2
|
12天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
37 1
|
14天前
|
JavaScript
如何使用内存快照分析工具来分析Node.js应用的内存问题?
需要注意的是,不同的内存快照分析工具可能具有不同的功能和操作方式,在使用时需要根据具体工具的说明和特点进行灵活运用。
35 3
|
28天前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
1月前
|
Web App开发 JavaScript 前端开发
使用 Chrome 浏览器的内存分析工具来检测 JavaScript 中的内存泄漏
【10月更文挑战第25天】利用 Chrome 浏览器的内存分析工具,可以较为准确地检测 JavaScript 中的内存泄漏问题,并帮助我们找出潜在的泄漏点,以便采取相应的解决措施。
188 9
|
2月前
|
并行计算 算法 IDE
【灵码助力Cuda算法分析】分析共享内存的矩阵乘法优化
本文介绍了如何利用通义灵码在Visual Studio 2022中对基于CUDA的共享内存矩阵乘法优化代码进行深入分析。文章从整体程序结构入手,逐步深入到线程调度、矩阵分块、循环展开等关键细节,最后通过带入具体值的方式进一步解析复杂循环逻辑,展示了通义灵码在辅助理解和优化CUDA编程中的强大功能。
|
2月前
|
程序员 编译器 C语言
动态内存分配与管理详解(附加笔试题分析)(下)
动态内存分配与管理详解(附加笔试题分析)(下)
55 2
|
3月前
|
算法 程序员 Python
程序员必看!Python复杂度分析全攻略,让你的算法设计既快又省内存!
在编程领域,Python以简洁的语法和强大的库支持成为众多程序员的首选语言。然而,性能优化仍是挑战。本文将带你深入了解Python算法的复杂度分析,从时间与空间复杂度入手,分享四大最佳实践:选择合适算法、优化实现、利用Python特性减少空间消耗及定期评估调整,助你写出高效且节省内存的代码,轻松应对各种编程挑战。
53 1