MongoDB記憶體使用原理

2021-06-18 00:20:26 字數 4258 閱讀 5718

但凡初次接觸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

在linux裡(別的系統也差不多),記憶體有物理記憶體和虛擬記憶體之說,物理記憶體是什麼自然無需解釋,虛擬記憶體實際是物理記憶體的抽象,多數情況下,出於方便性的考慮,程式訪問的都是虛擬記憶體位址,然後作業系統會通過page table機制把它翻譯成物理記憶體位址,詳細說明可以參考understanding memory和understanding virtual memory,至於程式是如何使用虛擬記憶體的,可以參考playing with virtual memory,這裡就不多費口舌了。

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

檢視記憶體情況最常用的是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演算法淘汰冷資料。還有乙個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 26070160

shell> sar -w

pswpin/s pswpout/s

0.00 0.00

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

目前,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 unlimited

shell> ulimit -v unlimited

注:ulimit的使用是有上下文的,最好放在mongodb的啟動指令碼裡。

有時候,mongodb連線數過多的話,會拖累效能,可以通過serverstatus查詢連線數:

mongo> db.serverstatus().connections
每個連線都是乙個執行緒,需要乙個stack,linux下預設的stack設定一般比較大:

shell> ulimit -a | grep stack

stack size (kbytes, -s) 10240

至於mongodb實際使用的stack大小,可以用如下命令確認(單位:k):

shell> cat /proc/$(pidof mongod)/limits | grep stack | awk -f 'size' ''
如果stack過大(比如:10240k)的話沒有意義,簡單對照命令結果中的size和rss:

shell> cat /proc/$(pidof mongod)/smaps | grep 10240 -a 10
所有連線消耗的記憶體加起來會相當驚人,推薦把stack設定小一點,比如說1024:

shell> ulimit -s 1024
注:從mongodb1.8.3開始,mongodb會在啟動時自動設定stack。

有時候,出於某些原因,你可能想釋放掉mongodb占用的記憶體,不過前面說了,記憶體管理工作是由虛擬記憶體管理器控制的,幸好可以使用mongodb內建的closealldatabases命令達到目的:

mongo> use admin

mongo> db.runcommand()

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

shell> sysctl -w vm.drop_caches=1
平時可以通過mongo命令列來監控mongodb的記憶體使用情況,如下所示:

mongo> db.serverstatus().mem:

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

shell> mongostat

940g 1893g 21.9g 0

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

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

res:占用的物理記憶體大小

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

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

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

mongo> db.stats()

本例中索引只有1g多,記憶體完全能裝下,而資料檔案則達到了1t,估計很難找到這麼大記憶體,此時保證記憶體能裝下熱資料即可,至於熱資料是多少,取決於具體的應用,你也可以通過觀察faults的大小來判斷當前記憶體是否能夠裝下熱資料,如果faults持續變大,就說明當前記憶體已經不能滿足熱資料的大小了。如此一來記憶體大小就明確了:記憶體 > 索引 + 熱資料,最好有點富餘,畢竟作業系統本身正常運轉也需要消耗一部分記憶體。

關於mongodb與記憶體的話題,大家還可以參考官方文件中的相關介紹。

mongodb記憶體對映原理

原理記憶體對映,是把檔案中資料全部對映到記憶體中的嗎?還是只是對映一部分內容,那麼這部門內容又是如何確定呢?看這張圖 映 就不需要讀磁碟嗎,沒有磁碟io嗎?沒有記憶體對映會怎麼樣 程序呼叫read,write的系統呼叫函式,核心程序把磁碟的資料讀到核心空間,然後在copy到使用者程序空間。沒錯,就是...

限制mongodb記憶體的使用

預設情況下,mongodb占用的記憶體大小為 starting in 3.4,the wiredtiger internal cache,by default,will use the larger of either 50 of ram minus 1 gb,or 256 mb.即 總記憶體 50...

限制MongoDB使用記憶體大小

因為mongodb的記憶體是系統的虛擬記憶體管理的,mongodb並不干涉記憶體管理工作,這樣雖然可以簡化mongo的工作,但同時mongo的記憶體使用是沒法控制的。真的沒法控制?辦法是有的,可以通過ulimit 來控制使用者程序的虛擬位址空間的大小。ulimit 是控制著所有程序的記憶體大小,怎麼...