我們公司的基礎(chǔ)架構(gòu)部有個云Redis平臺,其中Redis實例在申請的時候可以自由選擇需要的內(nèi)存的大小。然后就引發(fā)了我的一個思考,Redis單實例內(nèi)存最大申請到多大比較合適?假設(shè)母機(jī)是64GB內(nèi)存的物理機(jī),如果不考慮CPU資源的的浪費(fèi),我是否可以開一個50G的Redis實例?
于是我在Google上各種搜索,討論這個問題的人似乎不多。找到唯一感覺靠譜點的答案,那就是單進(jìn)程分配的內(nèi)存最好不要超過一個node里的內(nèi)存總量,否則linux當(dāng)該node里的內(nèi)存分配光了的時候,會在自己node里動用硬盤swap,而不是其它node里申請。這即使所謂的numa陷阱,當(dāng)Redis進(jìn)入這種狀態(tài)后會導(dǎo)致性能急劇下降(不只是redis,所有的內(nèi)存密集型應(yīng)用如mysql,mongo等都會有類似問題)。
看起來這個解釋非常有說服力。于是乎,我就想親手捕捉一次NUMA陷阱,看看這個家伙究竟什么樣。
1 先聊聊QPI與NUMA
最早在CPU都是單核的時候,用的總線都是FSB總線。經(jīng)典結(jié)構(gòu)如下圖:
圖1 單核時代的FSB總線
到來后來CPU的開發(fā)者們發(fā)現(xiàn)CPU的頻率已經(jīng)接近物理極限了,沒法再有更大程度的提高了。在2003年的時候,CPU的頻率就已經(jīng)達(dá)到2個多GB,甚至3個G了?,F(xiàn)在你再來看今天的CPU,基本也還是這個頻率,沒進(jìn)步多少。摩爾定律失效了,或者說是向另外一個方向發(fā)展了。那就是多核化、多CPU化。
圖2 多核時代的FSB總線
剛開始核不多的時候,F(xiàn)SB總線勉強(qiáng)還可以支撐。但是隨著CPU越來越多,所有的數(shù)據(jù)IO都通過這一條總線和內(nèi)存交換數(shù)據(jù),這條FSB就成為了整個計算機(jī)系統(tǒng)的瓶頸。舉個北京的例子,這就好比進(jìn)回龍觀的京藏高速,剛開始回龍觀人口不多的時候,這條高速承載沒問題。但是現(xiàn)在回龍觀聚集了幾十萬人了,“總線”還僅有這一條,未免效率太低。
CPU的設(shè)計者們很快改變了自己的設(shè)計,引入了QPI總線,相應(yīng)的CPU的結(jié)構(gòu)就叫NMUA架構(gòu)。下圖直觀理解
圖3 QPI總線
2 話說NUMA陷阱
NUMA陷阱指的是引入QPI總線后,在計算機(jī)系統(tǒng)里可能會存在的一個坑。大致的意思就是如果你的機(jī)器打開了numa,那么你的內(nèi)存即使在充足的情況下,也會使用磁盤上的swap,導(dǎo)致性能低下。原因就是NUMA為了高效,會僅僅只從你的當(dāng)前node里分配內(nèi)存,只要當(dāng)前node里用光了(即使其它node還有),也仍然會啟用硬盤swap。
當(dāng)我第一次聽說到這個概念的時候,不禁感嘆我運(yùn)氣好,我的Redis實例貌似從來沒有掉進(jìn)這個陷阱里過。那為了以后也別栽坑,趕緊去了解了下我的機(jī)器的numa狀態(tài):
上面結(jié)果說明我們有兩個node,node0和node1,分別有12個核心,各有32GB的內(nèi)存。再看zone_reclaim_mode,它用來管理當(dāng)一個內(nèi)存區(qū)域(zone)內(nèi)部的內(nèi)存耗盡時,是從其內(nèi)部進(jìn)行內(nèi)存回收還是可以從其他zone進(jìn)行回收的選項:
0 關(guān)閉zone_reclaim模式,可以從其他zone或NUMA節(jié)點回收內(nèi)存
1 打開zone_reclaim模式,這樣內(nèi)存回收只會發(fā)生在本地節(jié)點內(nèi)
2在本地回收內(nèi)存時,可以將cache中的臟數(shù)據(jù)寫回硬盤,以回收內(nèi)存
4 在本地回收內(nèi)存時,表示可以用Swap 方式回收內(nèi)存
3 實踐捕捉numa陷阱未遂
那我的好奇心就來了,既然我的單個node節(jié)點只有32G,那我部署一個50G的Redis,給它填滿數(shù)據(jù)試試到底會不會發(fā)生swap。
實驗開始,我先查看了本地總內(nèi)存,以及各個node的內(nèi)存剩余狀況。
總內(nèi)存不用解釋,/proc/zoneinfo里包含了node可供應(yīng)用程序申請的free pages。node1有4651908個頁面,4651908*4K=18G的可用內(nèi)存。接下來讓我們啟動redis實例,把其內(nèi)存上限設(shè)置到超過單個node里的內(nèi)存大小。我這里單node內(nèi)存大小是32G,我把redis設(shè)置成了50G。開始灌入數(shù)據(jù)。最終數(shù)據(jù)全部灌完之后,
實驗證明,在zone_reclaim_mode為1的情況下,Redis是平均在兩個node里申請節(jié)點的,并沒有固定在某一個cpu里。
莫非是大佬們的忠告錯了嗎?其實不是,如果不綁定親和性的話,分配內(nèi)存是當(dāng)進(jìn)程在哪個node上的CPU發(fā)起內(nèi)存申請,就優(yōu)先在哪個node里分配內(nèi)存。之所以是平均分配在兩個node里,是因為redis-server進(jìn)程實驗中經(jīng)常會進(jìn)入主動睡眠狀態(tài),醒來后可能CPU就換了。所以基本上,最后看起來內(nèi)存是平均分配的。如下圖,CPU進(jìn)行了500萬次的上下文切換,用top命令看到cpu也是在node0和node1跳來跳去。
4 綁定親和性,成功捕獲NUMA陷阱
殺死進(jìn)程,內(nèi)存歸位
綁定CPU和內(nèi)存的親和性,然后再啟動。
numactl --cpunodebind=0 --membind=0 /search/odin/daemon/redis/bin/redis-server /search/odin/daemon/redis/conf/redis.conf top命令觀察到CPU確實一直在node0的節(jié)點里。node里的內(nèi)存也在快速消耗。
看,內(nèi)存很快就消耗光了。我們再看top命令觀察到的swap,很激動地發(fā)現(xiàn),我終于陷入到傳說中的numa陷阱了。
這時候,Redis實際使用的物理內(nèi)存RES定格到了30g不再上漲,而是開始消耗Swap。又過了一會兒,Redis被oom給kill了。
5 結(jié)論
通過今天的實驗,我們可以發(fā)現(xiàn)確實有NUMA陷阱這種東西存在。不過那是我手工通過numactl指令綁定cpu和mem的親和性后才遭遇的。相信國內(nèi)絕大部分的線上Redis沒有進(jìn)行這個綁定,所以理論上來單Redis單實例可以使用到整個機(jī)器的物理內(nèi)存。(實踐中最好不要這么干,你的大部分內(nèi)存都綁定到一個redis進(jìn)程里的話,那其它CPU核就沒啥事干了,浪費(fèi)了CPU的多核計算能力)
6 擴(kuò)展
當(dāng)通過numactl綁定CPU和mem都在一個node里的時候,內(nèi)存IO不需要經(jīng)過總線,性能會比較高,你Redis的QPS能力也會上漲。和跨node的內(nèi)存IO性能對比,可以下面的實例,就是10:21的區(qū)別。
你要是對性能有極致的追求,可以試著綁定numa的親和性玩玩。不過,no作no die,掉到numa陷阱里可別賴我,嘎嘎!
審核編輯:劉清
-
Linux系統(tǒng)
+關(guān)注
關(guān)注
4文章
595瀏覽量
27449 -
總線
+關(guān)注
關(guān)注
10文章
2894瀏覽量
88223 -
FSB
+關(guān)注
關(guān)注
0文章
7瀏覽量
9415 -
Redis
+關(guān)注
關(guān)注
0文章
376瀏覽量
10898
發(fā)布評論請先 登錄
相關(guān)推薦
評論