10gen在mongodb的部署指南上,提到了在NUMA機器上,mongodb可能會出現問題,參見:http://docs.mongodb.org/manual/administration/production-notes/#production-numa
里面引用了一篇博客,主要講MySQL的swap insanity,而mongodb也會遇到類似的問題,博客地址:http://blog.jcole.us/2010/09/28/mysql-swap-insanity-and-the-numa-architecture/。
這篇博客有點長,我這里簡單概括一下。
對於單CPU,多核心的情況,每個核心訪問內存的速度是一樣的,這種架構稱為SMP(Symmetric multiprocessing, 對稱多處理器),又叫UMA(Uniform Memory Architecture,與NUMA相對,一致性內存訪問架構)。
對於多CPU,多核心的情況,如下圖:
可以看到,每個CPU都有一組配套的內存槽。每個CPU訪問自身的內存插槽,速度都很快,但對於主板上的其他內存插槽,訪問速度就會下降。這種架構被稱為NUMA。
對於Linux來說,加載的時候就會檢測內存,計算CPU到內存的訪問開銷,將CPU和內存分成一組組的。每個進程和線程,都會繼承父進程的NUMA策略,這種策略包括這個進程/線程會在哪個CPU上運行,分配的內存應該用哪組插槽的。
面對內存分配,只要一經分配到指定的CPU—內存槽,就不會再挪動了。對於數據庫這類應用,理想情況下是一個單一的多線程進程,吃掉了幾乎所有的系統內存,並盡可能多的消耗其余的系統資源例如IO。
對於兩個CPU的NUMA架構來說,如果一個核心分配的內存超過系統內存的一半,就會出現問題。而Linux的分配策略是,首先使用CPU 0,然后再使用CPU 1。這時候就會出現一種情況,CPU 0的內存組已經率先使用完了,但系統還有很多空閑內存,都在CPU 1上。這時候,Linux會選擇將CPU 0的內存刷到磁盤上,以換取可用內存。但是,swap過程遠比跨CPU訪問內存要慢啊。這就會造成內存還沒用光,但數據庫瘋狂刷盤的現象了。
解決辦法是用numactl指定分配策略,將數據庫需要的內存分散在各個CPU/內存組上,保證不會出現一個核心已滿而另一個核心空閑的情況。
#numactl --interleave all command