记一次mongodb内存使用过高的排查解决方案
最近经常收到mongodb一个从库内存使用率过高的告警,于是进行排查和解决,下面是过程
# 查看mongo服务使用的内存大小
db.serverStatus().mem
{ "bits" : 64,
"resident" : 23127, # 物理内存,单位是MB
"virtual" : 29093, # 虚拟内存,单位是MB
"supported" : true }
# 这个是查看wiredTiger占用了多少内存
db.serverStatus().wiredTiger.cache
{
"application threads page read from disk to cache count" : 204298331,
"application threads page read from disk to cache time (usecs)" : 38747559298,
"application threads page write from cache to disk count" : 106092771,
"application threads page write from cache to disk time (usecs)" : 17318222221,
"bytes allocated for updates" : 296788997,
"bytes belonging to page images in the cache" : 16482438247,
"bytes belonging to the history store table in the cache" : 64156976,
"bytes currently in the cache" : 17193353577, # 占用的内存,实际缓存的数据大小
"bytes dirty in the cache cumulative" : NumberLong("4525111900725"),
"bytes not belonging to page images in the cache" : 710915329,
"bytes read into cache" : NumberLong("8704327108782"),
"bytes written from cache" : NumberLong("10127087031660"),
......
}
db.serverStatus().tcmalloc
{
"generic" : {
"current_allocated_bytes" : NumberLong("16824344000"), # 已分配的内存
"heap_size" : NumberLong("28263317504")
},
"tcmalloc" : {
"pageheap_free_bytes" : NumberLong("8540606464"), # 堆空闲内存占用,这个较大的话需要回收
"pageheap_unmapped_bytes" : NumberLong(2132684800),
"max_total_thread_cache_bytes" : NumberLong(1073741824),
"current_total_thread_cache_bytes" : 117897648,
"total_free_bytes" : 765682240, # 总空闲内存
"central_cache_free_bytes" : 647679184,
"transfer_cache_free_bytes" : 105408,
"thread_cache_free_bytes" : 117897648,
"aggressive_memory_decommit" : 0,
"pageheap_committed_bytes" : NumberLong("26130632704"),
"pageheap_scavenge_count" : 13037917,
"pageheap_commit_count" : 16333243,
"pageheap_total_commit_bytes" : NumberLong("4585848258560"),
"pageheap_decommit_count" : 13687337,
"pageheap_total_decommit_bytes" : NumberLong("4559717625856"),
"pageheap_reserve_count" : 8988,
"pageheap_total_reserve_bytes" : NumberLong("28263317504"),
"spinlock_total_delay_ns" : NumberLong("75789350902"),
"release_rate" : 1,
"formattedString" : "------------------------------------------------\nMALLOC: 16824344576 (16044.9 MiB) Bytes in use by application\nMALLOC: + 8540606464 ( 8145.0 MiB) Bytes in page heap freelist\nMALLOC: + 647679184 ( 617.7 MiB) Bytes in central cache freelist\nMALLOC: + 105408 ( 0.1 MiB) Bytes in transfer cache freelist\nMALLOC: + 117897072 ( 112.4 MiB) Bytes in thread cache freelists\nMALLOC: + 100925440 ( 96.2 MiB) Bytes in malloc metadata\nMALLOC: ------------\nMALLOC: = 26231558144 (25016.4 MiB) Actual memory used (physical + swap)\nMALLOC: + 2132684800 ( 2033.9 MiB) Bytes released to OS (aka unmapped)\nMALLOC: ------------\nMALLOC: = 28364242944 (27050.2 MiB) Virtual address space used\nMALLOC:\nMALLOC: 482469 Spans in use\nMALLOC: 301 Thread heaps in use\nMALLOC: 4096 Tcmalloc page size\n------------------------------------------------\nCall ReleaseFreeMemory() to release freelist memory to the OS (via madvise()).\nBytes released to the OS take up virtual address space but no physical memory.\n"
}
}
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.
# 可以看到pageheap_free_bytes占了大头。
解释一下:
Virtual address space used 是mongo总的使用的虚拟内存。
Actual memory used (physical + swap)是mongo总的使用的实际内存。(我没有开swap)
实际内存又分成两部分,freelist中的和非freelist的。freelist的就是已经分配后来又用完释放的内存,
存在这个freelist数据结构中,已备后面重用这些内存,我的理解就是我用完了,但是我先拿着。
这样后面的业务来了,mongo就不需要再向os申请分配内存这一步了,从性能和效率的维度来看更好。
但是缺点是内存一直没有还给os,导致os角度来看,内存的使用率很高。
tcmalloc 为性能考虑,每个线程会有自己的 local free page cache,还有 central free page cache;
内存申请时,按 local thread free page cache ==> central free page cache 查找可用内存,找不到可用内存时才会从堆上申请;
当释放内存时,也会归还到 cache 里,tcmalloc 后台慢慢再归还给 OS操作系统,
多数情况下,内存使用率高的原因是 tcmalloc 未能及时将内存归还给操作系统,导致内存最大可能达到几十GB。
mongo为了提高性能,倾向于利用os上尽可能多的内存。
解决办法:
所以可以将freelist的内存及时释放给os,可以解决内存水位过高的问题。
db.adminCommand({setParameter:1,tcmallocAggressiveMemoryDecommit:1})
tcmallocAggressiveMemoryDecommit 是一个服务器参数,用于控制 TCMalloc 内存分配器在什么程度上积极地将不再使用的内存释放回操作系统。
当设置为 1(开启状态)时,tcmallocAggressiveMemoryDecommit 会使 TCMalloc 更积极地释放不再使用的内存。
这意味着当应用程序释放内存后,TCMalloc 会尝试将这部分内存标记为空闲并返回给操作系统,
而不是保留在进程的地址空间中以便快速重用。

浙公网安备 33010602011771号