使用內存的,答案自然就清楚了。
據說帶着問題學習更有效,那就先看一個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與內存的話題,大家還可以參考官方文檔中的相關介紹。
- 但凡初次接觸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 %MEM
- 1892g 21g 21g 69.6
- 這臺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,視大小可能有性能問題。
- 在上面的結果中,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,估計很難找到這麼大內存,此時保證內存能裝下熱數據即可,至於熱數據有多少,這就是個比例問題了,取決於具體的應用。如此一來內存大小就明確了:內存 > 索引 + 熱數據。