MongoDB對記憶體的嚴重佔用以及解決方法

chenfeng發表於2015-12-11

剛開始使用mongodb的時候,不太注意mongodb的記憶體使用,但透過查資料發現mongodb對記憶體的佔用是巨大的,在本地測試伺服器中,8G的記憶體居然被佔用了45%。汗呀。

本文就來剖析一下mongodb對記憶體的具體使用方法,以及生產環境針對mongodb佔大量記憶體的問題的解決。

先看一個MongoDB伺服器的top命令結果


 shell>top -p $(pidof mongod)
Mem:  32872124k total, 30065320k used,  2806804k free,   245020k buffers 
Swap:  2097144k total,      100k used,  2097044k free, 26482048k cached
VIRT    RES   SHR   %MEM
1892g  21g   21g     69.6

或者 先top後,然後 shift+m 把當前進場按佔用記憶體的多少排序。看看你的mongodb能佔用多少記憶體。

 

先了解一下linux對記憶體的管理方式:

Linux裡(別的系統也差不多),記憶體有實體記憶體和虛擬記憶體之說,實體記憶體是什麼自然無需解釋,虛擬記憶體實際是實體記憶體的抽象,多數情況下,出於方便性的考慮,程式訪問的都是虛擬記憶體地址,然後作業系統會把它翻譯成實體記憶體地址。

很多人會把虛擬記憶體和Swap混為一談,實際上Swap只是虛擬記憶體引申出的一種技術而已:作業系統一旦實體記憶體不足,為了騰出記憶體空間存放新內容,就會把當前實體記憶體中的內容放到交換分割槽裡,稍後用到的時候再取回來,需要注意的是,Swap的使用可能會帶來效能問題,偶爾為之無需緊張,糟糕的是實體記憶體和交換分割槽頻繁的發生資料交換,這被稱之為Swap顛簸,一旦發生這種情況,先要明確是什麼原因造成的,如果是記憶體不足就好辦了,加記憶體就可以解決,不過有的時候即使記憶體充足也可能會出現這種問題,比如MySQL就有可能出現這樣的情況,解決方法是限制使用Swap

shell> sysctl -w vm.swappiness=0

檢視記憶體情況最常用的是free命令:

shell> free -m
             total       used       free     shared    buffers     cached
Mem:         32101      29377       2723          0        239      25880
-/+ buffers/cache:       3258      28842
Swap:         2047          0       2047

新手看到used一欄數值偏大,free一欄數值偏小,往往會認為記憶體要用光了。其實並非如此,之所以這樣是因為每當我們操作檔案的時候,Linux都會盡可能的把檔案快取到記憶體裡,這樣下次訪問的時候,就可以直接從記憶體中取結果,所以cached一欄的數值非常的大,不過不用擔心,這部分記憶體是可回收的,作業系統會按照LRU演算法淘汰冷資料。除了cached,還有一個buffers,它和cached類似,也是可回收的,不過它的側重點在於緩解不同裝置的操作速度不一致造成的阻塞,這裡就不多做解釋了。

知道了原理,我們就可以推算出系統可用的記憶體是free + buffers + cached

shell> echo "2723 + 239 + 25880" | bc -l
28842

至於系統實際使用的記憶體是used buffers cached

shell> echo "29377 - 239 - 25880" | bc -l
3258

除了free命令,還可以使用sar命令:

shell> sar -r
kbmemfree kbmemused  %memused kbbuffers  kbcached
  3224392  29647732     90.19    246116  26070160
  3116324  29755800     90.52    245992  26157372
  2959520  29912604     91.00    245556  26316396
  2792248  30079876     91.51    245680  26485672
  2718260  30153864     91.73    245684  26563540
 
shell> sar -W
pswpin/s pswpout/s
    0.00      0.00
    0.00      0.00
    0.00      0.00
    0.00      0.00
    0.00      0.00

希望你沒有被%memused嚇到,如果不幸言中,請參考free命令的解釋。

 

接著我們們分析一下mongodb是怎麼使用記憶體的:

 

目前,MongoDB使用的是記憶體對映儲存引擎,它會把磁碟IO操作轉換成記憶體操作,如果是讀操作,記憶體中的資料起到快取的作用,如果是寫操作,記憶體還可以把隨機的寫操作轉換成順序的寫操作,總之可以大幅度提升效能。MongoDB並不干涉記憶體管理工作,而是把這些工作留給作業系統的虛擬快取管理器去處理,這樣的好處是簡化了MongoDB的工作,但壞處是你沒有方法很方便的控制MongoDB佔多大記憶體,事實上MongoDB會佔用所有能用的記憶體,所以最好不要把別的服務和MongoDB放一起。

 

有時候,即便MongoDB使用的是64位作業系統,也可能會遭遇臭名昭著的OOM問題,出現這種情況,多半是因為限制了虛擬記憶體的大小所致,可以這樣檢視當前值:

 

shell> ulimit -a | grep 'virtual'

多數作業系統預設都是把它設定成unlimited的,如果你的作業系統不是,可以這樣修改:

 

shell> ulimit -v unlimited

不過要注意的是,ulimit的使用是有上下文的,最好放在MongoDB的啟動指令碼里。

 

有時候,出於某些原因,你可能想釋放掉MongoDB佔用的記憶體,不過前面說了,記憶體管理工作是由虛擬記憶體管理器控制的,所以通常你只能透過重啟服務來釋放記憶體,你一定不齒於這樣的方法,幸好可以使用MongoDB內建的closeAllDatabases命令達到目的:

 

mongo> use admin

mongo> db.runCommand({closeAllDatabases:1})

另外,透過調整核心引數drop_caches也可以釋放快取:

 

shell> sysctl -w vm.drop_caches=1

平時可以透過mongo命令列來監控MongoDB的記憶體使用情況,如下所示:

 

mongo> db.serverStatus().mem

{

    "resident" : 22346,

    "virtual" : 1938524,

    "mapped" : 962283

}

還可以透過mongostat命令來監控MongoDB的記憶體使用情況,如下所示:

shell> mongostat

mapped  vsize    res faults

 940g 1893g 21.9g      0

 940g 1893g 21.9g      0

 940g 1893g 21.9g      0

 940g 1893g 21.9g      0

 940g 1893g 21.9g      0

 

其中記憶體相關欄位的含義是:

mapped:對映到記憶體的資料大小

visze:佔用的虛擬記憶體大小

res:實際使用的記憶體大小

注:如果操作不能再記憶體中完成,結果faults列的數值不會是0,視大小可能有效能問題。

 

在上面的結果中,vsizemapped的兩倍,而mapped等於資料檔案的大小,所以說vsize是資料檔案的兩倍,之所以會這樣,是因為本例中,MongoDB開啟了journal,需要在記憶體裡多對映一次資料檔案,如果關閉journal,則vsizemapped大致相當。

 

如果想驗證這一點,可以在開啟或關閉journal後,透過pmap命令來觀察檔案對映情況:

shell> pmap $(pidof mongod)

到底MongoDB配備多大記憶體合適?寬泛點來說,多多益善,如果要確切點來說,這實際取決於你的資料及索引的大小,記憶體如果能夠裝下全部資料加索引是最佳情況,不過很多時候,資料都會比記憶體大,比如本文說涉及的MongoDB例項:

mongo> db.stats()

{

        "dataSize" : 1004862191980,

        "indexSize" : 1335929664

}

本例中索引只有1G多,記憶體完全能裝下,而資料檔案則達到了1T,估計很難找到這麼大記憶體,此時保證記憶體能裝下熱資料即可,至於熱資料有多少,這就是個比例問題了,取決於具體的應用。如此一來記憶體大小就明確了:記憶體 > 索引 + 熱資料。

 

根據以上的分析我們可以得出幾點結論:

1.      mongodb 直接用作業系統的記憶體管理器來管理記憶體。而作業系統採用的是演算法淘汰冷資料。

2.      mongodb可以用重啟服務、調整核心引數以及mongodb內部的語法去清理mongodb對記憶體的快取。可能存在的問題是:這幾種清理方式都是全部清理,這樣的話mongodb的記憶體快取就失效了。

3.      mongodb 對記憶體的使用是可以被監控的,在生產環境中要定時的去監控這些資料。

4.      mongodb 對記憶體這種佔用方式使其儘量的和其他佔用記憶體的業務分開部署,例如memcahesphinxmysql等。

5.      作業系統中的交換分割槽swap 如果操作頻繁的話,會嚴重降低系統效率。要解決可以禁用交換分割槽,以及增加記憶體以及做分散式。

6.  生產環境中mongodb所在的主機應該儘量的大記憶體。

該文章轉自洪荒聽雨的部落格。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/15498/viewspace-1870420/,如需轉載,請註明出處,否則將追究法律責任。

相關文章