情况发生

线上环境使用yum安装的mongo4.4.3版本,已经使用了一年左右,这几天用户量访问暴涨了一波,收到服务器内存超80%的告警,经过排查发现存储占了100MB的数据,居然吃内存吃到20GB,可怕!

因为还有用户正在使用,所以只能在夜深人静的时候使用“重启大法”的时候,重启了mongo,内存占用恢复正常。

万幸的是服务上线前对Mongo连接池的中断测试,重启速度很快,未对服务造成比较大影响。

初步排查

因为线上环境已经重启了,没有保留什么有用的证据,比如当时mongostat,数据库的信息等…

第一步就先看了一下官方文档

官方文档

mongo 3.2.x以上的版本,默认使用的是wiredTiger,可以在启动参数下做如下设置

storage:
  wiredTiger:
   engineConfig:
     cacheSizeGB: <number>
       journalCompressor: <string>
       directoryForIndexes: <boolean>
       maxCacheOverflowFileSizeGB: <number>  // Deprecated in MongoDB 4.4
   collectionConfig:
     blockCompressor: <string>
   indexConfig:
     prefixCompression: <boolean>

主要是看storage.wiredTiger.engineConfig.cacheSizeGB,这个参数设置的是wiredTiger引擎可以使用内存的大小,mongo在3.2的版本之前,默认引擎是MMAP,3.2版本之后,默认的是wiredTiger,因为我们的版本是4.4.2,所以就不从引擎区别上去深究了。

我们在这波重启的时候,就把这个引擎的参数加上了16G,最终引擎能用到(16-1)/2=7.5GB,对于一个只占有100MB存储的数据库来说,应该够了。在这里官方文档表示,wriedTriger引擎会按两个值来算他引擎占用的内存值:

未设置cacheSizeGB的情况下:

  1. 会看系统内存,会使用到(系统内存-1GB)/2。
  2. 使用256MB(老版本是1G,后来为了照顾小机器下调了)
  3. 两者取较大的来使用。

设置了cacheSizeGB的情况下,按上面16GB的算法来算

但是我们仍然无法确定是否设置了这个参数,就能解决mongo占内存的问题。因为已经有很多人在网络说这个参数限制不了内存继续占用的问题。

本地测试

于是在本地开发环境重新安装了一个跟线上版本一模一样的mongo,设置了cacheSizeGB的大小为1GB,开始测试。

因为目前的设计,将用户信息这种关键的信息存在了mongo,所以这次测试同时测试了一个集合存储5kw用户信息的查询速度表现。

测试步骤

第一步,我用java+python在用户表中加入了将近5千多万条数据,耗时大约半天。插入时速度不会有太大的影响,能保持在2000-3000条/s。在prometheus中看监控,发现内存稳步增长,最终能维持在1GB。

第二步,我通过一些组合查询,查询用户表的信息,大约二、三个小时,内存稍有上涨,但是最终定在了1.27GB。

第三步,我从用户表中删除了大约二到三百万数据,最终内存上涨到了1.86GB。

从这几步操作来看,佐证了storage.wiredTiger.engineConfig.cacheSizeGB确实限制不了mongo的内存上涨。

这个时候因为有其他事情要忙,大约两周后再看prometheus,看到如下内存曲线:

在这里插入图片描述

忽略掉当前内存,这是我后来调试参数时,重启了mongo,但在重启之前看到内存有逐步下降的曲线。

不过考虑到这段时间没有人用,也是正常现象。但是有些同学可能会问,为什么限制了cacheSizeGB内存还是会涨?

从测试表现来看,确实wiredTiger在插入的时候内存一直控制在限制之内。也确如其他文章写到的一样,在做查询或者做删除的时候,内存还会上涨。

对mongo内存的理解

1、mongo快与占内存这个问题确实同时存在,但想想也对,“又想马儿跑,又不让马儿不吃草”这种事情是做不到的。

2、如果集合数据量大,而索引设置的不对,也会造成内存占用超高,因为结果集都被扔到了内存。

3、mongo的wiredTiger使用的是google内存分配工具tcmalloc,这个玩意能理解的同学,可以去看一下tcmalloc的FAQ,阿里云有做一个工具,可以在操作系统层面释放内存,但是没有对外开放,这个方法ReleaseFreeMemory()在调用期间会锁住整个PageHeap,直到归还完成,所以线上需谨慎使用。

4、mongo的机制有点无赖,只向操作系统要内存,而不会管理内存,比较依赖系统自身的内存淘汰算法,所以我们经常能看到内存占用过高的表现

如何管控mongo内存

  1. 从使用上的管控:
    1)多注意一下是否有慢查询现象,慢查询会导致mongo内存占用升高。比如我插入5千万条数据后,查询一个唯一的用户名都要接近十秒,加完索引,毫秒即可返回。
    2)设置cacheSizeGB吧,能有点帮忙是一点。

  2. 从部署上的优化:
    官方表示,强烈建议mongo部署在单独的机器上,尽量不要与其他应用放在一起(看起来官方也有点hold不住)。

  3. 从系统层面的管控(如果mongo处理混合部署的状态):
    如果在CentOS中mongodb是以服务的方式启动的。即能用service mongodb start的方式启动资源限制用这个命令

systemctl set-property <servicename> <field>=<value>

CentOS7 内存的设置方法为:

systemctl set-property mongod1 MemoryLimit=10G

如果是以进程的方式启动,此时mongodb 占用内存很高怎么办呢?可以执行以下两条命令(未测试)

#sync
#echo 3 > /proc/sys/vm/drop_caches

最后关于内存占用的问题,mongo大中华区首席咨询官张耀星是这么问答的:

​ 1)你们为什么这么恨buff/cache啊?它是帮你加速访问磁盘的,为什么要清它?
​ 2)为什么要担心它上去啊?它又不会让你oom
​ 3)buff cache里的内存操作系统是可以根据需求随时,可以认为它就是可用内存。为什么会有buff/cache存在?因为操作系统认为空着也是空着,不如缓存点什么,万一命中了呢?换句话说,这是操作系统的行为,根本不在Mongo管理范围内。

​ 4)它不会影响其他进程,其他进程需要内存操作系统就会从这里面释放出来给他们用
​ 5)mongo占内存后发生OOM,不是由buff cache引起的,如果发生了OOM应该从不合理的锁或者其他方面找问题,附官方表示,如果mongod在unix上意外停止运行的进程,可以用下面的方式查看日志

sudo grep mongod /var/log/messages
sudo grep score /var/log/messages
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐