MongoDB 使用过多内存

Spi*_*Xel 41 mongodb memory wiredtiger

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

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

以下是htopshow dbs命令的结果。

在此处输入图片说明

显示数据库

我知道 mongodb 使用内存映射 IO,所以基本上操作系统处理内存中的缓存内容,当另一个进程请求空闲内存时,mongodb理论上应该释放其缓存的内存,但从我们所见,它没有。

OOM 开始杀死其他重要进程,例如 postgres、redis 等。(可以看出,为了克服这个问题,我们将 RAM 增加到 183GB,现在可以工作但非常昂贵。mongo 使用了 ~87GB 的 ram,几乎是整个数据集大小的 4 倍)

所以,

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

谢谢 !

Spi*_*Xel 32

好的,所以按照 loicmathieu 和 jstell 给出的线索,稍微挖掘一下,这些是我使用 WiredTiger 存储引擎发现的关于 MongoDB 的事情。如果有人遇到同样的问题,我就把它放在这里。

我提到的内存使用线程都属于 2012-2014 年,都早于 WiredTiger,并且描述了原始 MMAPV1 存储引擎的行为,该引擎没有单独的缓存或支持压缩。

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

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

  • 默认情况下,WiredTiger 不会在每次提交时同步数据,因此日志文件也在 RAM 中,这会对内存造成影响。还提到为了有效地使用 I/O,WiredTiger 将 I/O 请求(缓存未命中)分块在一起,这似乎也占用了一些 RAM(实际上脏页(已更改/更新的页面)有一个更新列表对它们存储在 Concurrent SkipList 中)。

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

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

  • MongoDB 本身消耗内存来处理开放连接、聚合、服务器端代码等

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

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

db.getSiblingDB('data_server').stats()
# OR
db.stats()
Run Code Online (Sandbox Code Playgroud)

结果如下:

{
    "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
}
Run Code Online (Sandbox Code Playgroud)

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

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

还有就是OOM的问题,为了克服这个问题,由于我们没有足够的资源来取出mongodb,我们降低了oom_score_adj,以防止OOM暂时杀死重要进程(意思是我们告诉OOM不要杀死我们的所需的过程)。

  • 我们有类似的问题。MongoDB 不断消耗 RAM。相似的比例。“oom_score_adj”解决方案是您想出的最好的解决方案吗? (2认同)

joe*_*dle 9

文档

您可能希望阅读有关 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 ...
Run Code Online (Sandbox Code Playgroud)

当我们遇到 RAM 问题时,那是因为我们的一个索引占用了过多的 RAM。所以在这里我将展示我们如何追踪它。

你的索引有多大?

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" }
Run Code Online (Sandbox Code Playgroud)

现在我们可以查看这个庞大集合的详细信息,看看它的哪些索引成本最高:

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

这可以让我们更好地了解哪些地方可以节省开支。

(在这种情况下,我们有一个createTime相当大的索引- 每个文档一个条目 - 我们决定没有它我们也能活下去。)


小智 7

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

至于为什么它大于 DB + 索引的大小,请记住 WiredTiger 将数据库压缩在磁盘上,并且还使用快照日志来记录文档更改。因此,WiredTiger 的实际大小是使用 show dbs * compression_ration + 快照日志大小的大小。所以几乎不可能知道确切的预期大小。

也请记住,工具,如toppshtop并没有显示真正使用的应用程序的内存,参考,以了解详细信息这个SOW的问题:/sf/ask/9191241/ - 应用程序或进程的使用

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

无论如何,作为一个最佳实践,如果你有一个很大的 Mongo 数据库,不要将它安装在与其他数据库共享的主机上,否则你会遇到很多困难,以防出现你在这里描述的问题,要知道谁真正导致了主机的问题。