Mongo崩溃crash, 报out of memory的问题分析与解决方案

1  问题描述

       应用程序和MongoDB运行时,数据量在100M以内,系统运行3天左右后,MongoDB报OOM的错误并退出。

       使用环境:

  • windows 10
  • Mongodb 3.4.2

       异常信息:

2019-05-09T19:20:44.186+0800 I CONTROL  [conn59] mongod.exe    ...\src\mongo\util\stacktrace_windows.cpp(239)                                   mongo::printStackTrace+0x43
2019-05-09T19:20:44.186+0800 I CONTROL  [conn59] mongod.exe    ...\src\mongo\util\signal_handlers_synchronous.cpp(332)                          mongo::reportOutOfMemoryErrorAndExit+0x90
2019-05-09T19:20:44.186+0800 I CONTROL  [conn59] mongod.exe    ...\src\mongo\util\allocator.cpp(51)                                             mongo::mongoRealloc+0x19
2019-05-09T19:20:44.186+0800 I CONTROL  [conn59] mongod.exe    ...\src\mongo\bson\util\builder.h(332)                                           mongo::_BufBuilder::grow_reallocate+0x195
2019-05-09T19:20:44.187+0800 I CONTROL  [conn59] mongod.exe    ...\src\mongo\rpc\legacy_reply_builder.cpp(84)                                   mongo::rpc::LegacyReplyBuilder::getInPlaceReplyBuilder+0x31
2019-05-09T19:20:44.187+0800 I CONTROL  [conn59] mongod.exe    ...\src\mongo\db\commands\dbcommands.cpp(1486)                                   mongo::Command::run+0xa7
2019-05-09T19:20:44.187+0800 I CONTROL  [conn59] mongod.exe    ...\src\mongo\db\commands\dbcommands.cpp(1443)                                   mongo::Command::execCommand+0xb9d
2019-05-09T19:20:44.187+0800 I CONTROL  [conn59] mongod.exe    ...\src\mongo\db\run_commands.cpp(73)                                            mongo::runCommands+0x4e4
2019-05-09T19:20:44.187+0800 I CONTROL  [conn59] mongod.exe    ...\src\mongo\db\instance.cpp(236)                                               mongo::`anonymous namespace'::receivedCommand+0x1d4
2019-05-09T19:20:44.187+0800 I CONTROL  [conn59] mongod.exe    ...\src\mongo\db\instance.cpp(614)                                               mongo::assembleResponse+0x7ba
2019-05-09T19:20:44.187+0800 I CONTROL  [conn59] mongod.exe    ...\src\mongo\db\service_entry_point_mongod.cpp(135)                             mongo::ServiceEntryPointMongod::_sessionLoop+0x159
2019-05-09T19:20:44.187+0800 I CONTROL  [conn59] mongod.exe    c:\program files (x86)\microsoft visual studio 14.0\vc\include\functional(212)   std::_Func_impl<,std::allocator,void,std::shared_ptr const & __ptr64>::_Do_call+0x43
2019-05-09T19:20:44.187+0800 I CONTROL  [conn59] mongod.exe    ...\src\mongo\transport\service_entry_point_utils.cpp(78)                        mongo::`anonymous namespace'::runFunc+0x1b3
2019-05-09T19:20:44.187+0800 I CONTROL  [conn59] mongod.exe    c:\program files (x86)\microsoft visual studio 14.0\vc\include\thr\xthread(247)  std::_LaunchPad >,std::default_delete > > > >::_Run+0x75
2019-05-09T19:20:44.187+0800 I CONTROL  [conn59] mongod.exe    c:\program files (x86)\microsoft visual studio 14.0\vc\include\thr\xthread(210)  std::_Pad::_Call_func+0x9
2019-05-09T19:20:44.187+0800 I CONTROL  [conn59] ucrtbase.dll                                                                                   o_strcat_s+0x5e
2019-05-09T19:20:44.187+0800 I CONTROL  [conn59] KERNEL32.DLL                                                                                   BaseThreadInitThunk+0x14
2019-05-09T19:20:44.187+0800 F -        [conn59] out of memory.

2  Google上前人的回答及实验

  1. stackoverflow 的相似的问题
  2. 一个MongoDB经常crash并报OOM的解决方案和实验过程

       我重复了链接2的实验及验证其结果。将MongoDB的 Internal cache设置为256M,MongoDB在一个星期内没有发生过崩溃的情况。

      实验配置如下:

systemLog:
 destination: file
 path: "D:\\db.log"
 logAppend: true
storage:
 dbPath: "D:\\db"
 directoryPerDB: true
 journal:
  enabled: true
 wiredTiger:
  engineConfig:
   cacheSizeGB: 0.256
net:
 bindIp: 127.0.0.1
 port: 27017
security:
 authorization: disabled

3  结论预测

       MongoDB的Internal cache可通过--wiredTigerCacheSizeGB或者上述配置设置Internal cache的大小。
       MongoDB使用内存的方式不是从实例一开始就向操作系统申请了好了足够的内存空间,而是在运行的过程中,逐渐向操作系统申请内存的。例如,将Internal cache的大小设置为3G,而mongod实例刚启动时,它使用的内存仅有几十M或几百M。
       因此,MongoDB在运行过程中,会不停的向操作系统申请内存空间,直到涨到设置的最大值。然而,应用程序向操作系统申请内存是产生OOM的主要原因,存在与其它程序抢占资源或者在操作系统安全策略下不给申请内存的风险。
       导致MongoDB崩溃的代码如下:

类:allocator.cpp
源码函数:
void* mongoRealloc(void* ptr, size_t size) {
    void* x = std::realloc(ptr, size);
    if (x == NULL) {
        reportOutOfMemoryErrorAndExit();
    }
    return x;
}

       那么降低Internal cache的大小能避免MongoDB运行崩溃,是因为其操作在一定程度上避免了DB向操作系统申请更多的内存,仅在原有的内存空间重新分配内存。

4  如何设置Internal cache

        如果为了MongoDB不崩溃,把Internal cache设置在少量内存空间下运行,同样是不合理的,毕竟MongoDB是依靠大量的内存提高计算速率的。

(1) MongoDB的wiredTiger的使用内存情况

      首先,应该先了解下MongoDB的wiredTiger的使用内存情况。

      MongoDB 使用Internal cache 和 Filesystem cache两部分内存。

      默认的,Internal cache 取以下两个值的最大值:

50% of (RAM - 1 GB), or
256 MB.

       MongoDB的Filesystem cache默认会使用完所有的除Internal cache和其它进程未使用的内存。其策略取决于操作系统,尽可能的将使用过的数据缓存到Filesystem cache中。当内存不足时,使用LRU(最近最少使用算法)淘汰数据。Filesystem cache的管理其实质时操作系统的缓存管理。

(2) MongoDB的wiredTiger引擎读取与存储数据的过程与数据格式

        By default, WiredTiger uses Snappy block compression for all collections and prefix compression for all indexes. Compression defaults are configurable at a global level and can also be set on a per-collection and per-index basis during collection and index creation.

       Different representations are used for data in the WiredTiger internal cache versus the on-disk format:

  • Data in the filesystem cache is the same as the on-disk format, including benefits of any compression for data files. The filesystem cache is used by the operating system to reduce disk I/O.
  • Indexes loaded in the WiredTiger internal cache have a different data representation to the on-disk format, but can still take advantage of index prefix compression to reduce RAM usage. Index prefix compression deduplicates common prefixes from indexed fields.
  • Collection data in the WiredTiger internal cache is uncompressed and uses a different representation from the on-disk format. Block compression can provide significant on-disk storage savings, but data must be uncompressed to be manipulated by the server.

      Via the filesystem cache, MongoDB automatically uses all free memory that is not used by the WiredTiger cache or by other processes.

(3) MongoDB的 working set

       设置Internal cache的大小,与 MongoDB 的工作集紧密相关。

      什么是 working set

       Working set represents the total body of data that the application uses in the course of normal operation. Often this is a subset of the total data size, but the specific size of the working set depends on actual moment-to-moment use of the database.

       If you run a query that requires MongoDB to scan every document in a collection, the working set will expand to include every document. Depending on physical memory size.

       For best performance, the majority of your active set should fit in RAM.

       working set是DB所有数据的一个子集,它的大小取决于操作数据库的行为和时间。例如,一次查询的的所有数据。 为了最好的性能,大多数的常用的working set应该保留在内存中。

(4)设置Internal cache

       设置Internal cache应考虑多方面的因素。首先是MongoDB本身所在的操作系统环境,其操作环境中,是否有其它应用程序占用内存资源,占有后,服务器剩余的的空间大小如何,会超过设置的Internal cache的大小吗?然后是计算working set,使MongoDB能发挥其最好的性能。
       一般情况下,mongo所在的服务器不适合安装过多的应用程序。设置Internal cache的大小也不宜超过mongo的默认设置( 50% of (RAM - 1 GB) )。
       如果排除了操作系统中其它程序的的影响,我认为mongo的默认设置( 50% of (RAM - 1 GB) )是非常合理的,不用在配置上做任何修改。如果一定要指定Internal cache以特定值,应考虑上述两种因素,使MongoDB在稳定的,高性能的状态下运行。

5  资料参考

《Mongo Storage》

你可能感兴趣的:(DB)