线上环境使用yum安装的mongo4.4.3版本,已经使用了一年左右,这几天用户量访问暴涨了一波,收到服务器内存超80%的告警,经过排查发现存储占了100MB的数据,居然吃内存吃到20GB,可怕!
因为还有用户正在使用,所以只能在夜深人静的时候使用“重启大法”的时候,重启了mongo,内存占用恢复正常。
万幸的是服务上线前对Mongo连接池的中断测试,重启速度很快,未对服务造成比较大影响。
因为线上环境已经重启了,没有保留什么有用的证据,比如当时mongostat,数据库的信息等…
第一步就先看了一下官方文档
mongo 3.2.x以上的版本,默认使用的是wiredTiger,可以在启动参数下做如下设置
storage:
wiredTiger:
engineConfig:
cacheSizeGB:
journalCompressor:
directoryForIndexes:
maxCacheOverflowFileSizeGB: // Deprecated in MongoDB 4.4
collectionConfig:
blockCompressor:
indexConfig:
prefixCompression:
主要是看storage.wiredTiger.engineConfig.cacheSizeGB
,这个参数设置的是wiredTiger引擎可以使用内存的大小,mongo在3.2的版本之前,默认引擎是MMAP,3.2版本之后,默认的是wiredTiger,因为我们的版本是4.4.2,所以就不从引擎区别上去深究了。
我们在这波重启的时候,就把这个引擎的参数加上了16G,最终引擎能用到(16-1)/2=7.5GB,对于一个只占有100MB存储的数据库来说,应该够了。在这里官方文档表示,wriedTriger引擎会按两个值来算他引擎占用的内存值:
未设置cacheSizeGB的情况下:
设置了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在插入的时候内存一直控制在限制之内。也确如其他文章写到的一样,在做查询或者做删除的时候,内存还会上涨。
1、mongo快与占内存这个问题确实同时存在,但想想也对,“又想马儿跑,又不让马儿不吃草”这种事情是做不到的。
2、如果集合数据量大,而索引设置的不对,也会造成内存占用超高,因为结果集都被扔到了内存。
3、mongo的wiredTiger使用的是google内存分配工具tcmalloc,这个玩意能理解的同学,可以去看一下tcmalloc的FAQ,阿里云有做一个工具,可以在操作系统层面释放内存,但是没有对外开放,这个方法ReleaseFreeMemory()
在调用期间会锁住整个PageHeap,直到归还完成,所以线上需谨慎使用。
4、mongo的机制有点无赖,只向操作系统要内存,而不会管理内存,比较依赖系统自身的内存淘汰算法,所以我们经常能看到内存占用过高的表现
从使用上的管控:
1)多注意一下是否有慢查询现象,慢查询会导致mongo内存占用升高。比如我插入5千万条数据后,查询一个唯一的用户名都要接近十秒,加完索引,毫秒即可返回。
2)设置cacheSizeGB吧,能有点帮忙是一点。
从部署上的优化:
官方表示,强烈建议mongo部署在单独的机器上,尽量不要与其他应用放在一起(看起来官方也有点hold不住)。
从系统层面的管控(如果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