震惊,mongodb竟然是个吃内存大户

Posted by grt1stnull on 2019-03-06

mongodb 介绍

首先简单介绍一下 mongodb 。常见的数据库主要分为两种类型,分别是关系型数据库和非关系型数据库。前者的典型就是 mysql ,这个 web 的同学肯定都知道。非关系型数据库,即 nosql ,可以说以 mongodb 为典型吧。

关于数据库的选择

好的我们现在知道了 mysql 和 mongodb ,那么我们在不同的业务场景,怎么选择数据库呢?I have a mysql, I have a mongodb, oh, mysqlmongodb… OK,最常见的说法就是,如果需要存储的数据需要频繁的扩展,那么推荐使用 mongodb 。反正对我来讲,我肯定是 mysql、mysql、mysql 。老夫选存储就是一把梭,拿出 mysql 就是干。

mongodb 是一种文档型的数据库,和 mysql 的 insert xxx,xxx,xxx 不同,mongodb 可以直接插入 json 的数据,然后也可以对键值进行查询,所以用起来还是蛮方便的。

我在我的这个场景下面选择的是 mongodb ,为什么呢,我不是 mysql 一把梭吗?我才不会说因为毕设的需求里写的是 nosql 所以我用了 mongodb ,anyway,mongodb 对 json 的支持真的很棒。所以我真的觉得,对于 json 数据很方便。

mongodb 的搭建

docker 一键启动:docker run -p 27017:27017 -v <LocalDirectoryPath>:/data/db --name docker_mongodb -d mongo

python 下连接 mongodb 的话,用 pymongo 库。

要进入 shell 模式的话,命令行直接敲 mongo admin 。

我是这么发现 mongodb 吃内存的

首先,我在本地处理数据,然后咔咔插入 mongodb ,建立索引之后查询数据的效率也上去了,一切看起来都是那么的美好。直到,我在本地把 mongodb 的数据导出到文件,然后在校园网下龟速 scp 到腾讯云的学生机上,导入的时候我发现,导入到一小半,我的服务器 cpu 直接飙升到打满,现象就是不响应网络请求了。

因为我有过 docker 里面的 mysql 因为内存不足起不来的情况,所以我下意识的就觉得,妈的内存又不够了。等服务器恢复响应后,free -h 看一下,好像内存还行。

想进入 docker 里的 mongodb 看一下,结果发现容器已经崩了,我就把 docker 重启之后,再进入 mongodb 的命令行,发现数据只导入了几千条。

docker 里的 mongodb 虽然可以进入命令行,但是我还是觉得不方便,OK,那让我们去掉这层隔阂。一个 docker stop 再接一个 docker rm 我就把 mongodb 的容器给删了。应该是以前配置过源的缘故,一个 apt install 我就把 mongodb 装好了。

OK,现在本地有了,应该可以了。然后我就再次导入数据,结果发现运行到一半,服务器又卡住了。不出我所料的,又是服务器暂时没有响应,可能 cpu 飙升。于是机智的我决定把文件分割,从断了的地方开始继续,but,插入的时候好像不是按文件行的顺序插入的,所以可能不能断点续导入了。

不过没关系,重新分割好了。我用 split 把文件分割了一下split -l 30000 <filename> -d -a 4 <分割后的前缀>,然后挨个导入。结果尴尬的是,我导第二个文件的时候,导入又卡住了,手急眼快的我直接 ctrl c 终止运行,再 free -h 看一下内存,果然就剩几兆了。再看一下内存占用 ps aux|head -1;ps aux|grep -v PID|sort -rn -k +4|head 。妈的 mongodb 吃内存啊,内存不够,难怪导入总是导一半就卡了。

mongodb 吃内存怎么办

就比如我刚刚,总共有四个文件,但是因为服务器内存比较小,只能一个一个的文件导入,这时候怎么办呢?我用 systemctl restart mongodb 释放了 mongodb 的内存,然后最后数据都导入成功了,完美。

那么在部署 mongodb 的时候,怎么限制它占用的内存呢?就比如我这里,很明显我可以分给 mongodb 的内存是有限制的,然后我总结了两种限制内存的方法:

  1. 改  mongodb 的配置文件
  2. 使用 systemctl 限制

第一种方法:配置文件里可以加一个 wiredTigerCacheSizeGB ,通过这个来限制 mongodb 使用的内存。但是我这里,由于 mongodb 版本的问题,我这个键的值的类型只能为 int ,但是我学生服务器只有 1G 的内存,很明显我不能通过改配置文件来限制内存。

第二种方法,我一开始就想用这种方法。直接系统层面上限制,内存随便用,能超过限制算我输,操作方式也很简单:

1
2
systemctl set-property mongodb MemoryLimit=50M
systemctl daemon-reload

不过 50M 真的太小了,我的 mongodb 有时候直接就崩了 =。=

(实际上还有其他方法,感兴趣的同学可以了解一下

总结

得益于 mongodb 利用内存做缓存,使它具有着不错的效率。根据我的搜索得到的资料,mongodb 中不涉及对内存的管理,它把内存一切都交给系统来管理。(道理我都懂,但是大哥你用完能不能释放

因此,大家在(服务器上)使用 mongodb 的时候,一定要留意内存,别不小心就 oom 了。

参考资料

Docker MongoDB 部署
Python MongoDB