最近接到多個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,其實是在「好死」與「賴活着」的選擇,我的以爲,對於一些重要的業務場景來講,首先應該爲數據庫規劃足夠的內存,當內存不足時,「及時調整擴容」比「不可控的慢」更好。
其餘
本文爲雲棲社區原創內容,未經容許不得轉載。