MongoDB内存过高问题分析解决

<h2><div class="image-package"><img src="https://upload-images.jianshu.io/upload_images/5149787-9b10aa71b2bad3a8.jpeg" img-data="{"format":"jpeg","size":279883,"width":1920,"height":1280,"space":"srgb","channels":3,"depth":"uchar","density":72,"chromaSubsampling":"4:2:0","isProgressive":false,"hasProfile":false,"hasAlpha":false}" class="uploaded-img" style="min-height:200px;min-width:200px;" width="auto" height="auto"/>
</div></h2><h2><span>告警</span><span/><span> </span></h2><p>公司有个3.2.7版本的mongo复制集,最近几天频繁告警内存过高。</p><p>服务器配置16C+64G内存。mongo备节点内存使用到55G,触发告警。</p><p>以下内容基于3.2.7版本,3.2.7版本已经太老,很多后来的命令和配置,3.2.7都没有。</p><h2><span/><span>排查</span><span/><span> </span></h2><p>排查mongo配置</p><p>主要是检查cacheSizeGB</p> wiredTiger:
engineConfig:
cacheSizeGB: 20
<p>MongoDB 3.2 及以后,默认使用 WiredTiger 存储引擎,可通过 cacheSizeGB 选项配置 WiredTiger 引擎使用内存的上限,一般建议配置在系统可用内存的一班左右。</p><p>默认值是(RAM – 1GB) / 2。出发点是防止系统OOM kill。因为这里的配置只是wiredTiger的内存cache限额,并不是mongo的全部使用内存限额,整个mongo进程的内存占用要比这个值大,所以cacheSizeGB万万不可设置超过RAM的60%。</p><p>我们这里配置到了20G。但是实际运行中发现,在并发查询很高的情况下,wt的cacheSize还是会超过这个配置一点点。</p><p>查看mongo实例的内存使用情况</p>db.serverStatus().wiredTiger.cache
<p>返回结果中 bytes currently in the cache 后的值为缓存数据的大小</p>...
"bytes currently in the cache" : 21483838298,
...
<p>已经用满了,这种情况可以加一下内存了。但是内存太贵,业务也没有那么高的性能要求,保障不宕机是更有性价比的方案。</p><p>既然cache只用了20G,</p><p>看看tcmalloc的情况</p>db.serverStatus().tcmalloc
------------------------------------------------
MALLOC: 22351254936 (21315.8 MiB) Bytes in use by application
MALLOC: + 24922800128 (23768.2 MiB) Bytes in page heap freelist
MALLOC: + 449403872 ( 428.6 MiB) Bytes in central cache freelist
MALLOC: + 262144 ( 0.2 MiB) Bytes in transfer cache freelist
MALLOC: + 841870984 ( 802.9 MiB) Bytes in thread cache freelists
MALLOC: + 109572256 ( 104.5 MiB) Bytes in malloc metadata
MALLOC: ------------
MALLOC: = 48675164320 (46420.3 MiB) Actual memory used (physical + swap)
MALLOC: + 8663441408 ( 8262.1 MiB) Bytes released to OS (aka unmapped)
MALLOC: ------------
MALLOC: = 57338605728 (54682.4 MiB) Virtual address space used
MALLOC:
MALLOC: 378600 Spans in use
MALLOC: 1451 Thread heaps in use
MALLOC: 8192 Tcmalloc page size
------------------------------------------------
Call ReleaseFreeMemory() to release freelist memory to the OS (via madvise()).
Bytes released to the OS take up virtual address space but no physical memory.
<p>可以看到page heap freelist占了大头。</p><p>解释一下,57338605728 (54682.4 MiB) Virtual address space used 是mongo总的使用的虚拟内存。</p><p>48675164320 (46420.3 MiB) Actual memory used (physical + swap)是mongo总的使用的实际内存。(我没有开swap)</p><p>实际内存又分成两部分,freelist中的和非freelist的。freelist的就是已经分配后来又用完释放的内存,存在这个freelist数据结构中,已备后面重用这些内存,我的理解就是我用完了,但是我先拿着。这样后面的业务来了,mongo就不需要再向os申请分配内存这一步了,从性能和效率的维度来看更好。</p><p>但是缺点是内存一直没有还给os,导致os角度来看,内存的使用率很高。</p><p>tcmalloc 为性能考虑,每个线程会有自己的 local free page cache,还有 central free page cache;内存申请时,按 local thread free page cache ==> central free page cache 查找可用内存,找不到可用内存时才会从堆上申请;<strong>当释放内存时,也会归还到 cache 里,tcmalloc 后台慢慢再归还给 OS操作系统</strong>, 多数情况下,内存使用率高的原因是 tcmalloc 未能及时将内存归还给操作系统,导致内存最大可能达到几十GB。mongo为了提高性能,倾向于利用os上尽可能多的内存。</p><h2><span/><span>解决</span><span/><span> </span></h2><p>所以可以将freelist的内存及时释放给os,可以解决内存水位过高的问题。</p>db.adminCommand({setParameter:1,tcmallocAggressiveMemoryDecommit:1})
<p>tcmallocAggressiveMemoryDecommit 是一个服务器参数,用于控制 TCMalloc 内存分配器在什么程度上积极地将不再使用的内存释放回操作系统。当设置为 1(开启状态)时,tcmallocAggressiveMemoryDecommit 会使 TCMalloc 更积极地释放不再使用的内存。这意味着当应用程序释放内存后,TCMalloc 会尝试将这部分内存标记为空闲并返回给操作系统,而不是保留在进程的地址空间中以便快速重用。</p><p>
</p>

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容