MongoDB使用太多内存


28

我们已经使用MongoDB已有几周了,我们看到的总体趋势是mongodb使用了太多的内存(远远超过了其数据集+索引的总大小)。

我已经阅读了这个问题这个问题,但是似乎没有一个解决我所面临的问题,他们实际上是在解释文档中已经解释的内容。

以下是htopshow dbs命令的结果。

在此处输入图片说明

显示数据库

我知道mongodb使用内存映射的IO,因此基本上OS会处理内存中的缓存内容,并且从理论上讲,当另一个进程请求可用内存时,mongodb 应该释放其缓存的内存,但是从我们的观察来看,它不是。

OOM开始杀死其他重要进程,例如postgres,redis等。(可以看到,为解决此问题,我们将RAM增加到183GB,现在可以使用,但价格昂贵。mongo使用了约87GB的ram,几乎是整个数据集大小的4倍)

所以,

  1. 这样的内存使用量真的是正常的吗?(根据文档,WiredTiger最多将〜60%的RAM用于其缓存,但是考虑到数据集的大小,它甚至没有足够的数据量来占用86GB的RAM吗?)
  2. 即使预期内存使用量,在另一个进程开始请求更多内存的情况下,mongo为什么也不会放开分配的内存?在我们增加RAM并使系统完全不稳定之前,Linux oom不断将各种其他正在运行的进程(包括mongodb本身)杀死。

谢谢 !


4
也许关于WiredTiger内部的一些演示,例如mongodb.com/presentations/…,可能会有所启发。我希望物理RAM的默认使用率为50%,但这只是对专用MongoDB主机可能需要的内容的猜测,许多人都需要对其进行更改。FWIW,我不认为将cacheSizeGB设置为“限制” mongo -该选项在那里,因此您可以控制部署。确定缓存需要多少内存mongo,将需要您在预期的服务器负载下监视服务器缓存统计信息。

Answers:


23

好的,因此,在遵循了loicmathieu和jstell的提示并进行了一些深入研究之后,这些就是我使用WiredTiger存储引擎发现的有关MongoDB的内容。如果有人遇到同样的问题,我要放在这里。

我提到的内存使用线程全部属于2012-2014,所有线程都在WiredTiger之前,它们描述的是原始MMAPV1存储引擎的行为,该引擎没有单独的缓存或不支持压缩。

WiredTiger 缓存设置仅控制WiredTiger存储引擎直接使用的内存大小(而不是mongod使用的总内存)。MongoDB / WiredTiger配置中有许多其他事情可能占用内存,例如:

  • WiredTiger压缩磁盘存储,但内存中的数据未压缩。

  • 默认情况下,WiredTiger不会在每次提交时同步数据,因此日志文件也位于RAM中,这会占用大量内存。还提到了,为了有效地使用I / O,WiredTiger将I / O请求(缓存未命中)组合在一起,这似乎也占用了一些RAM(实际上,脏页(已更改/更新的页)具有更新列表)它们存储在 并发SkipList中)。

  • WiredTiger在其缓存中保留记录的多个版本(多版本并发控制,读取操作将在操作之前访问最后提交的版本)。

  • WiredTiger在缓存中保留数据的校验和。

  • MongoDB本身会消耗内存来处理打开的连接,聚合,服务器端代码等

考虑到这些事实,依赖在show dbs;技术上并不正确,因为它仅显示数据集的压缩大小。

可以使用以下命令来获取完整的数据集大小。

db.getSiblingDB('data_server').stats()
# OR
db.stats()

结果如下:

{
    "db" : "data_server",
    "collections" : 11,
    "objects" : 266565289,
    "avgObjSize" : 224.8413545621088,
    "dataSize" : 59934900658, # 60GBs
    "storageSize" : 22959984640,
    "numExtents" : 0,
    "indexes" : 41,
    "indexSize" : 7757348864, # 7.7GBs
    "ok" : 1
}

因此,似乎实际的数据集大小及其索引占用了大约68GB的内存。

考虑到所有这些因素,我想现在已经可以预期内存使用情况了,很好的部分是可以限制WiredTiger缓存大小,因为它可以非常有效地处理I / O操作(如上所述)。

OOM仍然存在问题,要解决此问题,因为我们没有足够的资源来删除mongodb,所以我们降低了oom_score_adj以防止OOM 暂时杀死重要进程(意思是我们告诉OOM不要杀死我们操作系统)。所需的流程)。


我们有一个类似的问题。MongoDB继续消耗RAM。相似的比例。oom_score_adj 解决方案是您想出的最好的解决方案吗?
Hartator

@Hartator好吧,我们减少了wiretiger的cacheSize,加大了对索引和索引策略的管理力度,最后,减少了我们关心的东西oom_score_adj,我想这一切都是可以完成的。
SpiXel

4

我认为MongoDB在这里没有问题,因为jstell告诉您,带有WiredTiger的MongoDB将使用50%的可用内存,因此,如果增加服务器的RAM,它将占用更多的内存。

由于它的大小超出了DB +索引的大小,因此请记住,WiredTiger压缩磁盘上的数据库,并使用快照日志记录文档更改。因此,WiredTiger的实际大小是使用show dbs * compression_ration +快照日志的大小的大小。因此,几乎不可能知道确切的预期大小。

也请记住,工具,如toppshtop并没有显示真正使用的应用程序的内存,参考,以了解详细信息这个SOW的问题:https://stackoverflow.com/questions/131303/how-to-measure-actual-memory应用程序或进程的使用

现在,回到您的问题。您在同一主机上运行其他工具,OOM会杀死它们。我对Linux OOM并不熟悉,但是您确定它会因为MongoDB或..仅仅因为它们而杀死它们(也许是因为Postgres占用了太多内存而杀死了Postgres)。

无论如何,作为最佳实践,如果您有一个大型的Mongo数据库,请不要将其安装在与其他数据库共享的主机中,否则,如果遇到类似此处所述的问题,您将遇到很多困难。真正导致主机出现问题的人


4

文件

您可能想阅读MongoDB的基本内存问题,以及有关检查内存使用情况的简短讨论

内存使用概述

该命令db.serverStatus()docs)可以概述内存使用情况,尤其是:

> db.serverStatus().mem
{ "bits" : 64, "resident" : 27, "virtual" : 397, "supported" : true }

> db.serverStatus().tcmalloc
... not easy to read! ...

> db.serverStatus().tcmalloc.tcmalloc.formattedString
------------------------------------------------
MALLOC:        3416192 (    3.3 MiB) Bytes in use by application
MALLOC: +      4788224 (    4.6 MiB) Bytes in page heap freelist
MALLOC: +       366816 (    0.3 MiB) Bytes in central cache freelist
...
... a bunch of stats in an easier to read format ...

您的索引有多大?

db.stats() 可以显示所有索引的总大小,但是我们还可以使用来获取单个集合的详细信息 db.myCollection.stats()

例如,此命令将比较每个集合的索引大小

> db.getCollectionNames().map(name => ({totalIndexSize: db.getCollection(name).stats().totalIndexSize, name: name})).sort((a, b) => a.totalIndexSize - b.totalIndexSize).forEach(printjson)
...
{ "totalIndexSize" : 696320, "name" : "smallCollection" }
{ "totalIndexSize" : 135536640, "name" : "bigCollection" }
{ "totalIndexSize" : 382681088, "name" : "hugeCollection" }
{ "totalIndexSize" : 511901696, "name" : "massiveCollection" }

现在,我们可以查看该大量集合的详细信息,以查看其哪些索引最昂贵:

> db.massiveCollection.stats().indexSizes
{
        "_id_" : 230862848,
        "groupId_1_userId_1" : 49971200,
        "createTime_1" : 180301824,
        "orderId_1" : 278528,
        "userId_1" : 50155520
}

这可以使我们更好地了解可能节省的地方。

(在这种情况下,我们有一个createTime相当大的索引-每个文档一个条目-并且我们决定没有它就可以生存。)


索引是否会消耗大量内存?
Mathias Lykkegaard Lorenzen

@MathiasLykkegaardLorenzen取决于相对于服务器RAM的索引字段的唯一值的数量。在我们的案例中,createTime索引是有问题的,因为它对于每个文档都是唯一的,并且该集合非常庞大。索引其他字段是可以的,因为唯一值较少(这些值是聚类的)。
joeytwiddle
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.