本文參考了以下文章、視訊:
- YouTube - What is Non Uniform Memory Access?
- The MySQL “swap insanity” problem and the effects of the NUMA architecture
- wikipedia - Non-uniform memory access
- NUMA與UEFI
一句話
NUMA 指的是針對某個 CPU,記憶體訪問的距離和時間是不一樣的。是為了解決多 CPU 系統下共享 BUS 帶來的效能問題。(這句話可能不太嚴謹,不是為了解決,而是事實上解決了。)
NUMA 架構圖
從最簡單的開始,一個 CPU(注意:這裡指的是物理 CPU,不是核。需要注意的一點是NUMA 是針對多物理 CPU 而言的,而不是多核。),通過 bus 和 RAM 相連。
接下來多CPU 出現了(再說一次,不是多核單CPU!),如果還是像之前那樣將所有的CPU 通過一個 BUS 和 RAM 相連,BUS 會成為效能的殺手。而且,加的 CPU 越多,效能損耗會越高。
這時候 NUMA 架構就展露身手了:通過把 CPU 和臨近的 RAM 當做一個 node,CPU 會優先訪問距離近的 RAM。同時,CPU 直接有一個快速通道連線,所以 每個CPU 還是訪問到所有的 RAM 位置(只是速度會有差異)。
實際中,一個不一定單獨佔有一個 RAM,可以有下面這些很多種組合:
淺看 Linux 中的NUMA架構
以下操作在阿里云云伺服器 Ubuntu18.04環境下執行
首先通過dmesg | grep -i numa
檢視一下系統是不是支援numa:可見當前系統不支援 :)
然後使用一個工具:numactl
,可以通過 apt install numactl
進行安裝。然後執行:
numactl --hardware
複製程式碼
還有一個實用工具:lstopo
,通過 apt install lstopo
lstopo --of png > server.png
複製程式碼
從圖中可以看到,有一個node 節點。為什麼我的系統不支援 numa,Linux 還是把所有的 CPU 和所有的 RAM 組合到一起成為一個 node 呢?這不是閒的沒事幹嗎?
關於這一點,《Understanding the Linux Kernel》一書裡面這麼說:
這主要是出於程式碼可擴充套件性的考慮,這樣一套程式碼就可以在不支援 numa 和支援 numa 的環境下執行了。
如果是支援 numa 架構的伺服器,看到的圖會是這樣的:
NUMA 對Linux 會產生什麼影響?
系統 boot 的時候,硬體會把 numa 資訊傳送給 os,如果系統支援 numa,會發生以下幾件事:
- 獲取 numa 配置資訊
- 將 processors(不是 cores) 分成很多 nodes,一般是一個 processor 一個 node。
- 將 processor 附近的 memory 分配給它。
- 計算node 間通訊的cost(距離)。
如果你只是把 CPU 和記憶體當作是黑盒子,簡單地期待它 work 的話,可能會發生意想不到的事情。
- 每個程式、執行緒都會繼承一個 numa policy,定義了可以使用那些CPU(甚至是那些 core),哪些記憶體可以使用,以及 policy 的強制程度,即是優先還是強制性只允許。
- 每個 thread 被分配到了一個”優先” 的 node 上面執行,thread 可以在其他地方執行(如果 policy 允許的話),但是 os 會嘗試讓他在優先地 node 上面去執行。
- 記憶體分配:預設記憶體從同一個 node 裡面進行分配。
- 在一個 node 上面分配地記憶體不會被移動到其他node。
上面兩段翻譯自: blog.jcole.us/2010/09/28/… ,如有不清晰的地方,請移步原文。
看一個MySQL 的例項
優質好文,有能力的同學最好還是直接看原文: blog.jcole.us/2010/09/28/… 我這裡簡單翻譯一下。
文中提到了一個問題:在一個有64G 記憶體、2個 4核 CPU 的 Linux 伺服器中執行 MySQL 服務,MySQL 配置了48G 的 innodb buffer pool。然後發現,儘管系統還有很多空餘的內容,很多記憶體被 swap 出去了。
這帶來了極大的效能問題,因為query 的時候如果需要的內容被 swap 出去了。。就需要再 load 回來。這也是困惱了MySQL 社群很長時間的問題。
前面說到 Linux 有一個 numa policy,這個是可以人為控制的。
—localalloc
,使用當前 node,預設。--preferred=node
,優先實用指定的 node,實在不行用其他的 nod 也可以。--membind=nodes
,總是使用人為指定一個或多個 nodes。--interleaved=all
,採用round-robin演算法輪流使用不同的 node。
從 Linux os 的角度來看,MySQL 資料庫就是一個程式,會優先讓他在某一個 node 中執行。如果只是使用少部分記憶體,這沒什麼問題,但是當你要佔用系統大多數記憶體的時候,問題就來了:
由於 os 會試圖讓你在某個『優先』的node裡面執行,就會產生記憶體分配不均勻的情況:
Node0 已經快被佔滿了,Node1還剩下很多。由於 node0和 node1是獨立的,儘管 node1裡面有空餘記憶體,node0裡面的記憶體還是會被 swap 出去。這就是前面提到的問題的根源。
那麼怎麼解決呢?
numactl --interleave all command
複製程式碼
用前面提到的--interleave all
numa policy,將這段新增到mysqld_save
語句前面。過後,記憶體分配就是均勻的了,記憶體足夠的情況下,就不會出現異常的 swap 現象了。
當然這只是一個最簡單粗暴的解決方案,還有其他更好的,原文有提及,但是不是本文的重點,所以就不深入探討了。
總結一下
摩爾定律正在失效,當 CPU 的效能總會有極限的那一天,多 CPU 才是未來。作為一個有理想的搬磚工人,至少還是應該多瞭解一下多 CPU 系統架構。作為一個系統軟體開發者,更應該熟悉多 CPU 架構,讓自己開發的應用充分利用硬體福利。
廣告時間,歡迎大家關注我的微信公眾號。同時本文同步於 github: github.com/liaochangji…