MongoDB 如何使用内存?为什么内存满了?
最近接到多個MongoDB內存方面的線上case及社區問題咨詢,主要集中在:
- 為什么我的 MongoDB 使用了 XX GB 內存?
- 一個機器上部署多個 Mongod 實例/進程,WiredTiger cache 應該如何配置?
- MongoDB 是否應該使用 SWAP 空間來降低內存壓力?
MongoDB 內存用在哪?
Mongod 進程啟動后,除了跟普通進程一樣,加載 binary、依賴的各種library 到內存,其作為一個DBMS,還需要負責客戶端連接管理,請求處理,數據庫元數據、存儲引擎等很多工作,這些工作都涉及內存的分配與釋放,默認情況下,MongoDB 使用 Google tcmalloc 作為內存分配器,內存占用的大頭主要是「存儲引擎」與 「客戶端連接及請求的處理」。
存儲引擎 Cache
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能力」。
TCP 連接及請求處理
MongoDB Driver 會跟 mongod 進程建立 tcp 連接,并在連接上發送數據庫請求,接受應答,tcp 協議棧除了為連接維護socket元數據為,每個連接會有一個read buffer及write buffer,用戶收發網絡包,buffer的大小通過如下sysctl系統參數配置,分別是buffer的最小值、默認值以及最大值,詳細解讀可以google。
net.ipv4.tcp_wmem = 8192 65536 16777216 net.ipv4.tcp_rmem = 8192 87380 16777216redhat7(redhat6上并沒有導出這么詳細的信息) 上通過?ss -m?可以查看每個連接的buffer的信息,如下是一個示例,讀寫 buffer 分別占了 2357478bytes、2626560bytes,即均在2MB左右;500個類似的連接就會占用掉 1GB 的內存;buffer 占到多大,取決于連接上發送/應答的數據包的大小、網絡質量等,如果請求應答包都很小,這個buffer也不會漲到很大;如果包比較大,這個buffer就更容易漲的很大。
tcp ESTAB 0 0 127.0.0.1:51601 127.0.0.1:personal-agentskmem:(r0,rb2357478,t0,tb2626560,f0,w0,o0,bl0)除了協議棧上的內存開銷,針對每個連接,Mongod 會起一個單獨的線程,專門負責處理這條連接上的請求,mongod 為處理連接請求的線程配置了最大1MB的線程棧,通常實際使用在幾十KB左右,通過 proc 文件系統看到這些線程棧的實際開銷。 除了處理請求的線程,mongod 還有一系列的后臺線程,比如主備同步、定期刷新 Journal、TTL、evict 等線程,默認每個線程最大ulimit -s(一般10MB)的線程棧,由于這批線程數量比較固定,占的內存也比較可控。
# cat /proc/$pid/smaps7f563a6b2000-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
- 如果一個機器上只部署 Mongod,mongod 可以使用所有可用內存,則是用默認配置即可。
- 如果機器上多個mongod混部,或者mongod跟其他的一些進程一起部署,則需要根據分給mongod的內存配額來配置?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,實際上是在「好死」與「賴活著」的選擇,個人覺得,對于一些重要的業務場景來說,首先應該為數據庫規劃足夠的內存,當內存不足時,「及時調整擴容」比「不可控的慢」更好。
其他
- 盡量減少內存排序的場景,內存排序一般需要更多的臨時內存
- 主備節點配置差距不要過大,備節點會維護一個buffer(默認最大256MB)用于存儲拉取到oplog,后臺從buffer里取oplog不斷重放,當備同步慢的時候,這個buffer會持續使用最大內存。
- 控制集合及索引的數量,減少databse管理元數據的內存開銷;集合、索引太多,元數據內存開銷是一方面的影響,更多的會影響啟動加載的效率、以及運行時的性能。
?
原文鏈接
本文為云棲社區原創內容,未經允許不得轉載。
總結
以上是生活随笔為你收集整理的MongoDB 如何使用内存?为什么内存满了?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于TensorFlow.js的Java
- 下一篇: 基于泛型编程的序列化实现方法