mongo db 與內存(轉)

但凡初次接觸MongoDB的人,無不驚訝於它對內存的貪得無厭,至於箇中緣由,我先講講Linux是如何管理內存的,再說說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 %MEM1892g 21g 21g 69.6


這臺MongoDB服務器有沒有性能問題?大家可以一邊思考一邊繼續閱讀。

先講講Linux是如何管理內存的

在Linux裏(別的系統也差不多),內存有物理內存和虛擬內存之說,物理內存是什麼自然無需解釋,虛擬內存實際是物理內存的抽象,多數

情況下,出於方便性的考慮,程序訪問的都是虛擬內存地址,然後操作系統會通過Page Table機制把它翻譯成物理內存地址,詳細說明可以

參考Understanding Memory和Understanding Virtual Memory,至於程序是如何使用虛擬內存的,可以參考Playing with Virtual Memory,這

裏就不多費口舌了。

很多人會把虛擬內存和Swap混爲一談,實際上Swap只是虛擬內存引申出的一種技術而已:操作系統一旦物理內存不足,爲了騰出內存空間

存放新內容,就會把當前物理內存中的內容放到交換分區裏,稍後用到的時候再取回來,需要注意的是,Swap的使用可能會帶來性能問題,

偶爾爲之無需緊張,糟糕的是物理內存和交換分區頻繁的發生數據交換,這被稱之爲Swap顛簸,一旦發生這種情況,先要明確是什麼原因造

成的,如果是內存不足就好辦了,加內存就可以解決,不過有的時候即使內存充足也可能會出現這種問題,比如MySQL就有可能出現這樣的

情況,一個可選的解決方法是限制使用Swap:

shell> sysctl -w vm.swappiness=0
查看內存情況最常用的是free命令:

shell> free -m total used free shared buffers cachedMem:

32101 29377 2723 0 239 25880

-/+ buffers/cache: 3258 28842

Swap: 2047 0 2047


新手看到used一欄數值偏大,free一欄數值偏小,往往會認爲內存要用光了。其實並非如此,之所以這樣是因爲每當我們操作文件的時

候,Linux都會盡可能的把文件緩存到內存裏,這樣下次訪問的時候,就可以直接從內存中取結果,所以cached一欄的數值非常的大,不過

不用擔心,這部分內存是可回收的,操作系統的虛擬內存管理器會按照LRU算法淘汰冷數據。還有一個buffers,也是可回收的,不過它是保

留給塊設備使用的。

知道了原理,我們就可以推算出系統可用的內存是free + buffers + cached:

shell> echo $((2723 + 239 + 25880))28842
至於系統實際使用的內存是used – buffers – cached:

shell> echo $((29377 - 239 - 25880))3258
除了free命令,還可以使用sar命令:

shell> sar -r

kbmemfree kbmemused %memused kbbuffers kbcached


3224392 29647732 90.19 246116 26070160shell> sar -Wpswpin/s pswpout/s 0.00 0.00

希望你沒有被%memused嚇到,如果不幸言中,重讀本文。

再說說MongoDB是如何使用內存的

目前,MongoDB使用的是內存映射存儲引擎,它會把數據文件映射到內存中,如果是讀操作,內存中的數據起到緩存的作用,如果是寫操

作,內存還可以把隨機的寫操作轉換成順序的寫操作,總之可以大幅度提升性能。MongoDB並不干涉內存管理工作,而是把這些工作留給操

作系統的虛擬內存管理器去處理,這樣做的好處是簡化了MongoDB的工作,但壞處是你沒有方法很方便的控制MongoDB佔多大內存,幸運

的是虛擬內存管理器的存在讓我們多數時候並不需要關心這個問題。

MongoDB的內存使用機制讓它在緩存重建方面更有優勢,簡而言之:如果重啓進程,那麼緩存依然有效,如果重啓系統,那麼可以通過拷貝

數據文件到/dev/null的方式來重建緩存,更詳細的描述請參考:Cache Reheating – Not to be Ignored。

有時候,即便MongoDB使用的是64位操作系統,也可能會遭遇OOM問題,出現這種情況,多半是因爲限制了內存的大小所致,可以這樣查

看當前值:

shell> ulimit -a | grep memory
多數操作系統缺省都是把它設置成unlimited的,如果你的操作系統不是,可以這樣修改:

shell> ulimit -m unlimitedshell> ulimit -v unlimited
注:ulimit的使用是有上下文的,最好放在MongoDB的啓動腳本里。

有時候,MongoDB連接數過多的話,會拖累性能,可以通過serverStatus查詢連接數:

mongo> db.serverStatus().connections
每個連接都是一個線程,需要一個Stack,Linux下缺省的Stack設置一般比較大:

shell> ulimit -a | grep stackstack size (kbytes, -s) 10240
至於MongoDB實際使用的Stack大小,可以用如下命令確認(單位:K):

shell> cat /proc/$(pidof mongod)/limits | grep stack | awk -F 'size' '{print int($NF)/1024}'
如果Stack過大(比如:10240K)的話沒有意義,簡單對照命令結果中的Size和Rss:

shell> cat /proc/$(pidof mongod)/smaps | grep 10240 -A 10
所有連接消耗的內存加起來會相當驚人,推薦把Stack設置小一點,比如說1024:

shell> ulimit -s 1024
注:從開始,MongoDB會在啓動時自動設置Stack。

有時候,出於某些原因,你可能想釋放掉MongoDB佔用的內存,不過前面說了,內存管理工作是由虛擬內存管理器控制的,幸好可以使用

MongoDB內置的closeAllDatabases命令達到目的:

mongo> use adminmongo> 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> mongostatmapped

vsize res faults

940g 1893g 21.9g 0

其中內存相關字段的含義是:

mapped:映射到內存的數據大小
visze:佔用的虛擬內存大小
res:佔用的物理內存大小
注:如果操作不能在內存中完成,結果faults列的數值不會是0,視大小可能有性能問題。

在上面的結果中,vsize是mapped的兩倍,而mapped等於數據文件的大小,所以說vsize是數據文件的兩倍,之所以會這樣,是因爲本例中,MongoDB開啓了journal,需要在內存裏多映射一次數據文件,如果關閉journal,則vsize和mapped大致相當。

如果想驗證這一點,可以在開啓或關閉journal後,通過pmap命令來觀察文件映射情況:

shell> pmap $(pidof mongod)
到底MongoDB配備多大內存合適?寬泛點來說,多多益善,如果要確切點來說,這實際取決於你的數據及索引的大小,內存如果能夠裝下全

部數據加索引是最佳情況,不過很多時候,數據都會比內存大,比如本文所涉及的MongoDB實例:

mongo> db.stats(){ "dataSize" : 1004862191980, "indexSize" : 1335929664}
本例中索引只有1G多,內存完全能裝下,而數據文件則達到了1T,估計很難找到這麼大內存,此時保證內存能裝下熱數據即可,至於熱數

據是多少,取決於具體的應用。如此一來內存大小就明確了:內存 > 索引 + 熱數據,最好有點富餘,畢竟操作系統本身正常運轉也需要消耗

一部分內存。

關於MongoDB與內存的話題,大家還可以參考官方文檔中的相關介紹。


  1. 但凡初次接觸MongoDB的人,無不驚訝於它對內存的貪得無厭,至於箇中緣由,我先講講Linux是如何管理內存的,再說說MongoDB是如何使用內存的,答案自然就清楚了。  
  2. 據說帶着問題學習更有效,那就先看一個MongoDB服務器的top命令結果:  
  3. shell> top -p $(pidof mongod)  
  4. Mem:  32872124k total, 30065320k used,  2806804k free,   245020k buffers  
  5. Swap:  2097144k total,      100k used,  2097044k free, 26482048k cached  
  6.   
  7.  VIRT  RES  SHR %MEM  
  8. 1892g  21g  21g 69.6  
  9. 這臺MongoDB服務器有沒有性能問題?大家可以一邊思考一邊繼續閱讀。  
  10. 先講講Linux是如何管理內存的  
  11. 在Linux裏(別的系統也差不多),內存有物理內存和虛擬內存之說,物理內存是什麼自然無需解釋,虛擬內存實際是物理內存的抽象,多數情況下,出於方便性的考慮,程序訪問的都是虛擬內存地址,然後操作系統會把它翻譯成物理內存地址。  
  12. 很多人會把虛擬內存和Swap混爲一談,實際上Swap只是虛擬內存引申出的一種技術而已:操作系統一旦物理內存不足,爲了騰出內存空間存放新內容,就會把當前物理內存中的內容放到交換分區裏,稍後用到的時候再取回來,需要注意的是,Swap的使用可能會帶來性能問題,偶爾爲之無需緊張,糟糕的是物理內存和交換分區頻繁的發生數據交換,這被稱之爲Swap顛簸,一旦發生這種情況,先要明確是什麼原因造成的,如果是內存不足就好辦了,加內存就可以解決,不過有的時候即使內存充足也可能會出現這種問題,比如MySQL就有可能出現這樣的情況,解決方法是限制使用Swap:  
  13. shell> sysctl -w vm.swappiness=0  
  14. 查看內存情況最常用的是free命令:  
  15. shell> free -m  
  16.              total       used       free     shared    buffers     cached  
  17. Mem:         32101      29377       2723          0        239      25880  
  18. -/+ buffers/cache:       3258      28842  
  19. Swap:         2047          0       2047  
  20. 新手看到used一欄數值偏大,free一欄數值偏小,往往會認爲內存要用光了。其實並非如此,之所以這樣是因爲每當我們操作文件的時候,Linux都會盡可能的把文件緩存到內存裏,這樣下次訪問的時候,就可以直接從內存中取結果,所以cached一欄的數值非常的大,不過不用擔心,這部分內存是可回收的,操作系統會按照LRU算法淘汰冷數據。除了cached,還有一個buffers,它和cached類似,也是可回收的,不過它的側重點在於緩解不同設備的操作速度不一致造成的阻塞,這裏就不多做解釋了。  
  21. 知道了原理,我們就可以推算出系統可用的內存是free + buffers + cached:  
  22. shell> echo "2723 + 239 + 25880" | bc -l  
  23. 28842  
  24. 至於系統實際使用的內存是used – buffers – cached:  
  25. shell> echo "29377 - 239 - 25880" | bc -l  
  26. 3258  
  27. 除了free命令,還可以使用sar命令:  
  28. shell> sar -r  
  29. kbmemfree kbmemused  %memused kbbuffers  kbcached  
  30.   3224392  29647732     90.19    246116  26070160  
  31.   3116324  29755800     90.52    245992  26157372  
  32.   2959520  29912604     91.00    245556  26316396  
  33.   2792248  30079876     91.51    245680  26485672  
  34.   2718260  30153864     91.73    245684  26563540  
  35.   
  36. shell> sar -W  
  37. pswpin/s pswpout/s  
  38.     0.00      0.00  
  39.     0.00      0.00  
  40.     0.00      0.00  
  41.     0.00      0.00  
  42.     0.00      0.00  
  43. 希望你沒有被%memused嚇到,如果不幸言中,請參考free命令的解釋。  
  44. 再說說MongoDB是如何使用內存的  
  45. 目前,MongoDB使用的是內存映射存儲引擎,它會把磁盤IO操作轉換成內存操作,如果是讀操作,內存中的數據起到緩存的作用,如果是寫操作,內存還可以把隨機的寫操作轉換成順序的寫操作,總之可以大幅度提升性能。MongoDB並不干涉內存管理工作,而是把這些工作留給操作系統的虛擬緩存管理器去處理,這樣的好處是簡化了MongoDB的工作,但壞處是你沒有方法很方便的控制MongoDB佔多大內存,事實上MongoDB會佔用所有能用的內存,所以最好不要把別的服務和MongoDB放一起。  
  46. 有時候,即便MongoDB使用的是64位操作系統,也可能會遭遇臭名昭著的OOM問題,出現這種情況,多半是因爲限制了虛擬內存的大小所致,可以這樣查看當前值:  
  47. shell> ulimit -a | grep 'virtual'  
  48. 多數操作系統缺省都是把它設置成unlimited的,如果你的操作系統不是,可以這樣修改:  
  49. shell> ulimit -v unlimited  
  50. 不過要注意的是,ulimit的使用是有上下文的,最好放在MongoDB的啓動腳本里。  
  51. 有時候,出於某些原因,你可能想釋放掉MongoDB佔用的內存,不過前面說了,內存管理工作是由虛擬內存管理器控制的,所以通常你只能通過重啓服務來釋放內存,你一定不齒於這樣的方法,幸好可以使用MongoDB內置的closeAllDatabases命令達到目的:  
  52. mongo> use admin  
  53. mongo> db.runCommand({closeAllDatabases:1})  
  54. 另外,通過調整內核參數drop_caches也可以釋放緩存:  
  55. shell> sysctl -w vm.drop_caches=1  
  56. 平時可以通過mongo命令行來監控MongoDB的內存使用情況,如下所示:  
  57. mongo> db.serverStatus().mem:  
  58. {  
  59.     "resident" : 22346,  
  60.     "virtual" : 1938524,  
  61.     "mapped" : 962283  
  62. }  
  63. 還可以通過mongostat命令來監控MongoDB的內存使用情況,如下所示:  
  64. shell> mongostat  
  65. mapped  vsize    res faults  
  66.   940g  1893g  21.9g      0  
  67.   940g  1893g  21.9g      0  
  68.   940g  1893g  21.9g      0  
  69.   940g  1893g  21.9g      0  
  70.   940g  1893g  21.9g      0  
  71. 其中內存相關字段的含義是:  
  72. mapped:映射到內存的數據大小  
  73. visze:佔用的虛擬內存大小  
  74. res:實際使用的內存大小  
  75. 注:如果操作不能再內存中完成,結果faults列的數值不會是0,視大小可能有性能問題。  
  76. 在上面的結果中,vsize是mapped的兩倍,而mapped等於數據文件的大小,所以說vsize是數據文件的兩倍,之所以會這樣,是因爲本例中,MongoDB開啓了journal,需要在內存裏多映射一次數據文件,如果關閉journal,則vsize和mapped大致相當。  
  77. 如果想驗證這一點,可以在開啓或關閉journal後,通過pmap命令來觀察文件映射情況:  
  78. shell> pmap $(pidof mongod)  
  79. 到底MongoDB配備多大內存合適?寬泛點來說,多多益善,如果要確切點來說,這實際取決於你的數據及索引的大小,內存如果能夠裝下全部數據加索引是最佳情況,不過很多時候,數據都會比內存大,比如本文說涉及的MongoDB實例:  
  80. mongo> db.stats()  
  81. {  
  82.         "dataSize" : 1004862191980,  
  83.         "indexSize" : 1335929664  
  84. }  
  85. 本例中索引只有1G多,內存完全能裝下,而數據文件則達到了1T,估計很難找到這麼大內存,此時保證內存能裝下熱數據即可,至於熱數據有多少,這就是個比例問題了,取決於具體的應用。如此一來內存大小就明確了:內存 > 索引 + 熱數據。 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章