溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

mongodb 對內(nèi)存的嚴(yán)重占用以及解決方法

發(fā)布時間:2020-04-10 10:09:11 來源:網(wǎng)絡(luò) 閱讀:4810 作者:洪荒聽雨 欄目:MongoDB數(shù)據(jù)庫

  剛開始使用mongodb的時候,不太注意mongodb的內(nèi)存使用,但通過查資料發(fā)現(xiàn)mongodb對內(nèi)存的占用是巨大的,在本地測試服務(wù)器中,8G的內(nèi)存居然被占用了45%。汗呀。

  本文就來剖析一下mongodb對內(nèi)存的具體使用方法,以及生產(chǎn)環(huán)境針對mongodb占大量內(nèi)存的問題的解決。

  先看一個MongoDB服務(wù)器的top命令結(jié)果

  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 把當(dāng)前進(jìn)場按占用內(nèi)存的多少排序??纯茨愕膍ongodb能占用多少內(nèi)存。

  先了解一下linux對內(nèi)存的管理方式:

  在Linux里(別的系統(tǒng)也差不多),內(nèi)存有物理內(nèi)存和虛擬內(nèi)存之說,物理內(nèi)存是什么自然無需解釋,虛擬內(nèi)存實際是物理內(nèi)存的抽象,多數(shù)情況下,出于方便性的考慮,程序訪問的都是虛擬內(nèi)存地址,然后操作系統(tǒng)會把它翻譯成物理內(nèi)存地址。

  很多人會把虛擬內(nèi)存和Swap混為一談,實際上Swap只是虛擬內(nèi)存引申出的一種技術(shù)而已:操作系統(tǒng)一旦物理內(nèi)存不足,為了騰出內(nèi)存空間存放新內(nèi)容,就會把當(dāng)前物理內(nèi)存中的內(nèi)容放到交換分區(qū)里,稍后用到的時候再取回來,需要注意的是,Swap的使用可能會帶來性能問題,偶爾為之無需緊張,糟糕的是物理內(nèi)存和交換分區(qū)頻繁的發(fā)生數(shù)據(jù)交換,這被稱之為Swap顛簸,一旦發(fā)生這種情況,先要明確是什么原因造成的,如果是內(nèi)存不足就好辦了,加內(nèi)存就可以解決,不過有的時候即使內(nèi)存充足也可能會出現(xiàn)這種問題,比如MySQL就有可能出現(xiàn)這樣的情況,解決方法是限制使用Swap:

  shell> sysctl -w vm.swappiness=0

  查看內(nèi)存情況最常用的是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一欄數(shù)值偏大,free一欄數(shù)值偏小,往往會認(rèn)為內(nèi)存要用光了。其實并非如此,之所以這樣是因為每當(dāng)我們操作文件的時候,Linux都會盡可能的把文件緩存到內(nèi)存里,這樣下次訪問的時候,就可以直接從內(nèi)存中取結(jié)果,所以cached一欄的數(shù)值非常的大,不過不用擔(dān)心,這部分內(nèi)存是可回收的,操作系統(tǒng)會按照LRU算法淘汰冷數(shù)據(jù)。除了cached,還有一個buffers,它和cached類似,也是可回收的,不過它的側(cè)重點在于緩解不同設(shè)備的操作速度不一致造成的阻塞,這里就不多做解釋了。

  知道了原理,我們就可以推算出系統(tǒng)可用的內(nèi)存是free + buffers + cached:

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

  28842

  至于系統(tǒng)實際使用的內(nèi)存是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是怎么使用內(nèi)存的:

  目前,MongoDB使用的是內(nèi)存映射存儲引擎,它會把磁盤IO操作轉(zhuǎn)換成內(nèi)存操作,如果是讀操作,內(nèi)存中的數(shù)據(jù)起到緩存的作用,如果是寫操作,內(nèi)存還可以把隨機的寫操作轉(zhuǎn)換成順序的寫操作,總之可以大幅度提升性能。MongoDB并不干涉內(nèi)存管理工作,而是把這些工作留給操作系統(tǒng)的虛擬緩存管理器去處理,這樣的好處是簡化了MongoDB的工作,但壞處是你沒有方法很方便的控制MongoDB占多大內(nèi)存,事實上MongoDB會占用所有能用的內(nèi)存,所以最好不要把別的服務(wù)和MongoDB放一起。

  有時候,即便MongoDB使用的是64位操作系統(tǒng),也可能會遭遇臭名昭著的OOM問題,出現(xiàn)這種情況,多半是因為限制了虛擬內(nèi)存的大小所致,可以這樣查看當(dāng)前值:

  shell> ulimit -a | grep 'virtual'

  多數(shù)操作系統(tǒng)缺省都是把它設(shè)置成unlimited的,如果你的操作系統(tǒng)不是,可以這樣修改:

  shell> ulimit -v unlimited

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

  有時候,出于某些原因,你可能想釋放掉MongoDB占用的內(nèi)存,不過前面說了,內(nèi)存管理工作是由虛擬內(nèi)存管理器控制的,所以通常你只能通過重啟服務(wù)來釋放內(nèi)存,你一定不齒于這樣的方法,幸好可以使用MongoDB內(nèi)置的closeAllDatabases命令達(dá)到目的:

  mongo> use admin

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

  另外,通過調(diào)整內(nèi)核參數(shù)drop_caches也可以釋放緩存:

  shell> sysctl -w vm.drop_caches=1

  平時可以通過mongo命令行來監(jiān)控MongoDB的內(nèi)存使用情況,如下所示:

  mongo> db.serverStatus().mem:

  {

  "resident" : 22346,

  "virtual" : 1938524,

  "mapped" : 962283

  }

  還可以通過mongostat命令來監(jiān)控MongoDB的內(nèi)存使用情況,如下所示:

  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

  其中內(nèi)存相關(guān)字段的含義是:

  mapped:映射到內(nèi)存的數(shù)據(jù)大小

  visze:占用的虛擬內(nèi)存大小

  res:實際使用的內(nèi)存大小

  注:如果操作不能再內(nèi)存中完成,結(jié)果faults列的數(shù)值不會是0,視大小可能有性能問題。

  在上面的結(jié)果中,vsize是mapped的兩倍,而mapped等于數(shù)據(jù)文件的大小,所以說vsize是數(shù)據(jù)文件的兩倍,之所以會這樣,是因為本例中,MongoDB開啟了journal,需要在內(nèi)存里多映射一次數(shù)據(jù)文件,如果關(guān)閉journal,則vsize和mapped大致相當(dāng)。

  如果想驗證這一點,可以在開啟或關(guān)閉journal后,通過pmap命令來觀察文件映射情況:

  shell> pmap $(pidof mongod)

  到底MongoDB配備多大內(nèi)存合適?寬泛點來說,×××,如果要確切點來說,這實際取決于你的數(shù)據(jù)及索引的大小,內(nèi)存如果能夠裝下全部數(shù)據(jù)加索引是最佳情況,不過很多時候,數(shù)據(jù)都會比內(nèi)存大,比如本文說涉及的MongoDB實例:

  mongo> db.stats()

  {

  "dataSize" : 1004862191980,

  "indexSize" : 1335929664

  }

  本例中索引只有1G多,內(nèi)存完全能裝下,而數(shù)據(jù)文件則達(dá)到了1T,估計很難找到這么大內(nèi)存,此時保證內(nèi)存能裝下熱數(shù)據(jù)即可,至于熱數(shù)據(jù)有多少,這就是個比例問題了,取決于具體的應(yīng)用。如此一來內(nèi)存大小就明確了:內(nèi)存 > 索引 + 熱數(shù)據(jù)。

  根據(jù)以上的分析我們可以得出幾點結(jié)論:

  1. mongodb 直接用操作系統(tǒng)的內(nèi)存管理器來管理內(nèi)存。而操作系統(tǒng)采用的是LRU算法淘汰冷數(shù)據(jù)。

  2. mongodb可以用重啟服務(wù)、調(diào)整內(nèi)核參數(shù)以及mongodb內(nèi)部的語法去清理mongodb對內(nèi)存的緩存??赡艽嬖诘膯栴}是:這幾種清理方式都是全部清理,這樣的話mongodb的內(nèi)存緩存就失效了。

  3. mongodb 對內(nèi)存的使用是可以被監(jiān)控的,在生產(chǎn)環(huán)境中要定時的去監(jiān)控這些數(shù)據(jù)。

  4. mongodb 對內(nèi)存這種占用方式使其盡量的和其他占用內(nèi)存的業(yè)務(wù)分開部署,例如memcahe,sphinx,mysql等。

  5. 操作系統(tǒng)中的交換分區(qū)swap 如果操作頻繁的話,會嚴(yán)重降低系統(tǒng)效率。要解決可以禁用交換分區(qū),以及增加內(nèi)存以及做分布式。

  6. 生產(chǎn)環(huán)境中mongodb所在的主機應(yīng)該盡量的大內(nèi)存。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI