最近接到多个MongoDB内存方面的线上case及社区问题咨询,主要集中在:mongodb
Mongod 进程启动后,除了跟普通进程同样,加载 binary、依赖的各类library 到内存,其做为一个DBMS,还须要负责客户端链接管理,请求处理,数据库元数据、存储引擎等不少工做,这些工做都涉及内存的分配与释放,默认状况下,MongoDB 使用 Google tcmalloc 做为内存分配器,内存占用的大头主要是「存储引擎」与 「客户端链接及请求的处理」。数据库
MongoDB 3.2 及之后,默认使用 WiredTiger 存储引擎,可经过 cacheSizeGB
选项配置 WiredTiger 引擎使用内存的上限,通常建议配置在系统可用内存的60%左右(默认配置)。后端
举个例子,若是 cacheSizeGB
配置为 10GB,能够认为 WiredTiger 引擎经过tcmalloc分配的内存总量不会超过10GB。为了控制内存的使用,WiredTiger 在内存使用接近必定阈值就会开始作淘汰,避免内存使用满了阻塞用户请求。缓存
目前有4个可配置的参数来支持 wiredtiger 存储引擎的 eviction 策略(通常不须要修改),其含义是:网络
参数 | 默认值 | 含义 |
---|---|---|
eviction_target | 80 | 当 cache used 超过 eviction_target ,后台evict线程开始淘汰 CLEAN PAGE |
eviction_trigger | 95 | 当 cache used 超过 eviction_trigger ,用户线程也开始淘汰 CLEAN PAGE |
eviction_dirty_target | 5 | 当 cache dirty 超过 eviction_dirty_target ,后台evict线程开始淘汰 DIRTY PAGE |
eviction_dirty_trigger | 20 | 当 cache dirty 超过 eviction_dirty_trigger , 用户线程也开始淘汰 DIRTY PAGE |
在这个规则下,一个正常运行的 MongoDB 实例,cache used 通常会在 0.8 * cacheSizeGB
及如下,偶尔超出问题不大;若是出现 used>=95% 或者 dirty>=20%,并一直持续,说明内存淘汰压力很大,用户的请求线程会阻塞参与page淘汰,请求延时就会增长,这时能够考虑「扩大内存」或者 「换更快的磁盘提高IO能力」。并发
MongoDB Driver 会跟 mongod 进程创建 tcp 链接,并在链接上发送数据库请求,接受应答,tcp 协议栈除了为链接维护socket元数据为,每一个链接会有一个read buffer及write buffer,用户收发网络包,buffer的大小经过以下sysctl系统参数配置,分别是buffer的最小值、默认值以及最大值,详细解读能够google。app
net.ipv4.tcp_wmem = 8192 65536 16777216 net.ipv4.tcp_rmem = 8192 87380 16777216
redhat7(redhat6上并无导出这么详细的信息) 上经过 ss -m
能够查看每一个链接的buffer的信息,以下是一个示例,读写 buffer 分别占了 2357478bytes、2626560bytes,即均在2MB左右;500个相似的链接就会占用掉 1GB 的内存;buffer 占到多大,取决于链接上发送/应答的数据包的大小、网络质量等,若是请求应答包都很小,这个buffer也不会涨到很大;若是包比较大,这个buffer就更容易涨的很大。socket
tcp ESTAB 0 0 127.0.0.1:51601 127.0.0.1:personal-agent skmem:(r0,rb2357478,t0,tb2626560,f0,w0,o0,bl0)
除了协议栈上的内存开销,针对每一个链接,Mongod 会起一个单独的线程,专门负责处理这条链接上的请求,mongod 为处理链接请求的线程配置了最大1MB的线程栈,一般实际使用在几十KB左右,经过 proc 文件系统看到这些线程栈的实际开销。 除了处理请求的线程,mongod 还有一系列的后台线程,好比主备同步、按期刷新 Journal、TTL、evict 等线程,默认每一个线程最大ulimit -s
(通常10MB)的线程栈,因为这批线程数量比较固定,占的内存也比较可控。tcp
# cat /proc/$pid/smaps 7f563a6b2000-7f563b0b2000 rw-p 00000000 00:00 0 Size: 10240 kB Rss: 12 kB Pss: 12 kB Shared_Clean: 0 kB Shared_Dirty: 0 kB Private_Clean: 0 kB Private_Dirty: 12 kB Referenced: 12 kB Anonymous: 12 kB AnonHugePages: 0 kB Swap: 0 kB KernelPageSize: 4 kB MMUPageSize: 4 kB
线程在处理请求时,须要分配临时buffer存储接受到的数据包,为请求创建上下文(OperationContext),存储中间的处理结果(如排序、aggration等)以及最终的应答结果等。性能
当有大量请求并发时,可能会观察到 mongod 使用内存上涨,等请求降下来后又慢慢释放的行为,这个主要是 tcmalloc 内存管理策略致使的,tcmalloc 为性能考虑,每一个线程会有本身的 local free page cache,还有 central free page cache;内存申请时,按 local thread free page cache ==> central free page cache 查找可用内存,找不到可用内存时才会从堆上申请;当释放内存时,也会归还到 cache 里,tcmalloc 后台慢慢再归还给 OS, 默认状况下,tcmalloc 最多会 cache min(1GB,1/8 * system_memory) 的内存, 经过 setParameter.tcmallocMaxTotalThreadCacheBytesParameter
参数能够配置这个值,不过通常不建议修改,尽可能在访问层面作调优)
tcmalloc cache的管理策略,MongoDB 层暴露了几个参数来调整,通常不须要调整,若是能清楚的理解tcmalloc原理及参数含义,可作针对性的调优;MongoDB tcmalloc 的内存状态能够经过 db.serverStatus().tcmalloc
查看,具体含义能够看 tcmalloc 的文档。重点能够关注下 total_free_bytes
,这个值告诉你有多少内存是 tcmalloc 本身缓存着,没有归还给 OS 的。
mymongo:PRIMARY> db.serverStatus().tcmalloc { "generic" : { "current_allocated_bytes" : NumberLong("2545084352"), "heap_size" : NumberLong("2687029248") }, "tcmalloc" : { "pageheap_free_bytes" : 34529280, "pageheap_unmapped_bytes" : 21135360, "max_total_thread_cache_bytes" : NumberLong(1073741824), "current_total_thread_cache_bytes" : 1057800, "total_free_bytes" : 86280256, "central_cache_free_bytes" : 84363448, "transfer_cache_free_bytes" : 859008, "thread_cache_free_bytes" : 1057800, "aggressive_memory_decommit" : 0, ... } }
合理配置 WiredTiger cacheSizeGB
cacheSizeGB
,按配额的60%左右配置便可。控制并发链接数
TCP链接对 mongod 的内存开销上面已经详细分析了,不少同窗对并发有必定误解,认为「并发链接数越高,数据库的QPS就越高」,实际上在大部分数据库的网络模型里,链接数太高都会使得后端内存压力变大、上下文切换开销变大,从而致使性能降低。
MongoDB driver 在链接 mongod 时,会维护一个链接池(一般默认100),当有大量的客户端同时访问同一个mongod时,就须要考虑减少每一个客户端链接池的大小。mongod 能够经过配置 net.maxIncomingConnections
配置项来限制最大的并发链接数量,防止数据库压力过载。
是否应该配置 SWAP
官方文档上的建议以下,意思是配置一下swap,避免mongod由于内存使用太多而OOM。
For the WiredTiger storage engine, given sufficient memory pressure, WiredTiger may store data in swap space. Assign swap space for your systems. Allocating swap space can avoid issues with memory contention and can prevent the OOM Killer on Linux systems from killing mongod.
开启 SWAP 与否各有优劣,SWAP开启,在内存压力大的时候,会利用SWAP磁盘空间来缓解内存压力,此时整个数据库服务会变慢,但具体变慢到什么程度是不可控的。不开启SWAP,当总体内存超过机器内存上线时就会触发OOM killer把进程干掉,其实是在告诉你,可能须要扩展一下内存资源或是优化对数据库的访问了。
是否开启SWAP,其实是在「好死」与「赖活着」的选择,我的以为,对于一些重要的业务场景来讲,首先应该为数据库规划足够的内存,当内存不足时,「及时调整扩容」比「不可控的慢」更好。
其余
本文为云栖社区原创内容,未经容许不得转载。