什么是NUMA?
NUMA中,雖然內存直接attach在CPU上,但是由於內存被平均分配在了各個die上。只有當CPU訪問自身直接attach內存對應的物理地址時,才會有較短的響應時間(后稱Local Access)。而如果需要訪問其他CPU attach的內存的數據時,就需要通過inter-connect通道訪問,響應時間就相比之前變慢了(后稱Remote Access)。
所以NUMA(Non-Uniform Memory Access)就此得名:非一致訪問分布共享存儲技術
每個CPU模塊之間都是通過互聯模塊進行連接和信息交互,CPU都是互通互聯的,同時,每個CPU模塊平均划分為若干個Chip(不多於4個),每個Chip都有自己的內存控制器及內存插槽。
在NUMA中還有三個節點的概念:
1)、本地節點:對於某個節點中的所有CPU,此節點稱為本地節點。
2)、鄰居節點:與本地節點相鄰的節點稱為鄰居節點。
3)、遠端節點:非本地節點或鄰居節點的節點,稱為遠端節點。
4)、鄰居節點和遠端節點,都稱作非本地節點(Off Node)。
CPU訪問不同類型節點內存的速度是不相同的,訪問本地節點的速度最快,訪問遠端節點的速度最慢,即訪問速度與節點的距離有關,距離越遠訪問速度越慢,此距離稱作Node Distance。應用程序要盡量的減少不通CPU模塊之間的交互,如果應用程序能有方法固定在一個CPU模塊里,那么應用的性能將會有很大的提升。
二、NUMA實踐
1、安裝numactl工具
Linux提供了一個手工調優的命令numactl(默認不安裝)
#yum install numactl -y
#numactl --hardware 列舉系統上的NUMA節點
2、查看numa狀態
# numactl --show policy: default preferred node: current physcpubind: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 cpubind: 0 1 nodebind: 0 1 membind: 0 1
# numastat node0 node1 numa_hit 1296554257 918018444 numa_miss 8541758 40297198 numa_foreign 40288595 8550361 interleave_hit 45651 45918 local_node 1231897031 835344122 other_node 64657226 82674322
說明:
- numa_hit —命中的,也就是為這個節點成功分配本地內存訪問的內存大小
- numa_miss —把內存訪問分配到另一個node節點的內存大小,這個值和另一個node的numa_foreign相對應。
- numa_foreign –另一個Node訪問我的內存大小,與對方node的numa_miss相對應
- local_node ----這個節點的進程成功在這個節點上分配內存訪問的大小
- other_node ----這個節點的進程 在其它節點上分配的內存訪問大小
很明顯,miss值和foreign值越高,就要考慮綁定的問題。
3、numad服務
在redhat6中,有一個numad的服務(需手工安裝),它可以自動的監控我們cpu狀況,並自動平衡資源,這個服務需要在內存使用量非常大的時候才會有明顯的效果,當內存空余量較大時,需要關閉KSM,避免發生沖突。官方說在某些內存使用巨大的環境中,可能會提高50%的性能。
# service numad start
4、查看cpu和內存使用情況
# numactl --hardware available: 2 nodes (0-1) node 0 cpus: 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 node 0 size: 64337 MB node 0 free: 1263 MB node 1 cpus: 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 node 1 size: 64509 MB node 1 free: 30530 MB node distances: node 0 1 0: 10 21 1: 21 10
cpu0 可用 內存 1263 MB
cpu1 可用內存 30530 MB
當cpu0上申請內存超過1263M時必定使用swap,這個是很不合理的。
舉例:
這里假設我要執行一個java param命令,此命令需要1G內存;一個python param命令,需要8G內存。
最好的優化方案時python在node1中執行,而java在node0中執行,那命令是:
#numactl --cpubind=0 --membind=0 python param
#numactl --cpubind=1 --membind=1 java param
5、NUMA的內存分配策略
1.缺省(default):總是在本地節點分配(分配在當前進程運行的節點上);
2.綁定(bind):強制分配到指定節點上;
3.交叉(interleave):在所有節點或者指定的節點上交織分配;
4.優先(preferred):在指定節點上分配,失敗則在其他節點上分配。
因為NUMA默認的內存分配策略是優先在進程所在CPU的本地內存中分配,會導致CPU節點之間內存分配不均衡,當某個CPU節點的內存不足時,會導致swap產生,而不是從遠程節點分配內存。這就是所謂的swap insanity 現象。
舉例:
# numactl --hardware node 0 cpus: 0 2 4 6 node 0 size: 65490 MB node 0 free: 24447 MB node 1 cpus: 1 3 5 7 node 1 size: 65536 MB node 1 free: 16050 MB node distances: node 0 1 0: 10 20 1: 20 10
可以看到numa節點是2個,cpu物理節點是8個
現在我們綁定資源,兩顆cpu,每顆4個物理節點,那么我們開4個mysql實例,每個實例綁定2個cpu物理節點
numactl --physcpubind=0,3 --localalloc mysqld_multi --defaults-extra-file=/etc/mysqld_multi.cnf start 1
–physcpubind 指定綁定的cpu節點,
–localalloc表示使用內存方式,不交叉,以免降低性能,
mysqld_multi是mysql實例啟動命令
三、如何關閉NUMA
方法一:通過bios關閉
BIOS:interleave = Disable / Enable
方法二:通過OS關閉
1、編輯 /etc/default/grub 文件,加上:numa=off
GRUB_CMDLINE_LINUX="crashkernel=auto numa=off rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet"
注:參數只需加 numa=off 即可 (rd.lvm.lv是因為機器上做了lvm所以帶有對應參數)
2、重新生成 /etc/grub2.cfg 配置文件:
# grub2-mkconfig -o /etc/grub2.cfg
3、重啟操作系統
# reboot
4、確認:
# dmesg | grep -i numa [ 0.000000] Command line: BOOT_IMAGE=/vmlinuz-3.10.0-327.el7.x86_64 root=/dev/mapper/centos-root ro crashkernel=auto numa=off rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet[ 0.000000] NUMA turned off[ 0.000000] Kernel command line: BOOT_IMAGE=/vmlinuz-3.10.0-327.el7.x86_64 root=/dev/mapper/centos-root ro crashkernel=auto numa=off rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet
# cat /proc/cmdline BOOT_IMAGE=/vmlinuz-3.10.0-327.el7.x86_64 root=/dev/mapper/centos-root ro crashkernel=auto numa=off rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet