1. NUMA 的前世今生
NUMA
(
Non Uniform Memory Access
)和
UMA
(
Uniform Memory Access
)是两种 CPU 相关的硬件架构。在早期的 UMA 架构中,CPU 通过前端总线(FSB,
Front Side Bus
)连接到北桥芯片,然后北桥芯片连接到内存,即内存控制器集成在北桥芯片中。外部 IO 设备与南桥芯片相连,南桥芯片与北桥芯片通过内部总线相连。
下图为 UMA 架构图
下图为早期的 UMA 架构主板。图中,靠近 CPU 黄色框线处为北桥芯片,其内部集成了内存控制器,通过 FSB 前端总线与 CPU 互联。靠近 BIOS 电池红色框线处为南桥芯片,集成了外围 IO 设备的控制器(如USB、Audio等),通过内部总线与北桥芯片互联。
100%•75%•50%
总线模型保证了 CPU 的所有内存访问都是一致的,不必考虑不同内存地址之间的差异。优点是资源共享,而缺点是总线争用激烈。随着服务器中 CPU 数量的增多,FSB 总线争用的弊端慢慢越来越明显,成为了系统瓶颈。于是,为了消除 FSB 的瓶颈,Intel 在 Nehalem CPU 上推出了 NUMA 架构。AMD 在引入 64 位 x86 架构时,也实现了 NUMA 架构。
1.1 NUMA 中的概念
CPU socket
会有一个独立的内存控制器
CPU scoket
独立直连到一部分内存,这部分直连内存称为此 CPU 的 “本地内存”。
CPU socket
与其直连的 “本地内存”,可称为一个 “
Numa Node
”。
Quick Path Interconnect
)总线进行连接。CPU 可以通过 QPI 总线访问不与自己直连的 “
远程内存
”。
Numa Node
中的内存(称为 “
远程内存
”),则延迟增加,效率降低。
如下图所示,一台服务器包含 2 个处理器、4 个内存块。NUMA 架构将 1 个处理器与 2 个内存块合起来(如将
Processor1
与
Memory1.1
、
Memory1.2
结合)称为一个 “
NUMA Node
”。如此,这台服务器共有 2 个
NUMA Node
。在物理分布上,
NUMA Node
内的处理器和内存块的物理距离更小,因此访问速度也更快。如下图所示,
NUMA Node1
的
Processor1
访问
Memory1.1
和
Memory1.2
比访问
Memory2.1
和
Memory2.2
更快。所以,使用 NUMA 的模式,如果能尽量保证本 Node 内的 CPU 只访问本 Node 内的内存块,那这样的效率就是最高的。
100%•75%•50%
2. Linux 中的 NUMA
2.1 numactl 安装
Linux 提供了一个 NUMA 绑核工具 numactl(默认不安装),在 CentOS 中安装命令如下:
sudo yum install numactl −y
2.2 查看 CPU 架构
通过 “
lscpu
” 命令,可查看系统中的处理器是否为 NUMA 架构。~]$ sudo lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 64 On-line CPU(s) list: 0-63 Thread(s) per core: 2 Core(s) per socket: 16 Socket(s): 2 NUMA node(s): 2 Vendor ID: GenuineIntel CPU family: 6 Model: 85 Model name: Intel(R) Xeon(R) Silver 4216 CPU @ 2.10GHz Stepping: 7 CPU MHz: 2699.981 CPU max MHz: 3200.0000 CPU min MHz: 800.0000 BogoMIPS: 4200.00 Virtualization: VT-x L1d cache: 32K L1i cache: 32K L2 cache: 1024K L3 cache: 22528K NUMA node0 CPU(s): 0-15,32-47 NUMA node1 CPU(s): 16-31,48-63
结果显示,系统中包含 2 个
Numa Node
。其中,"0-15,32-47
" 核属于Numa Node1
、"16-31,48-63
" 属于Numa Node2
。通过如下的 “numactl --hardware
’’ 也可以验证这一信息。除此之外,”numactl --hardware
’'还展示了每个Numa Node
的本地内存分配,以及访问本地内存、远程内存的距离(distance)信息。
~]$ sudo numactl --hardware available: 2 nodes (0-1) node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 node 0 size: 31692 MB node 0 free: 29048 MB node 1 cpus: 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 node 1 size: 32234 MB node 1 free: 30114 MB node distances: node 0 1 0: 10 21 1: 21 10
2.3 Linux 中的绑核策略
~]$ sudo 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
示例中显示,当前绑核策略为 "default’’。"default’’ 默认策略为 "
--localalloc
’’。内存绑核策略包括:--localalloc
或者-l
:进程尝试从本地节点上请求分配内存,如果失败则从其他 Node 上申请内存。--membind=nodes
或者-m nodes
:规定进程只能从指定的 nodes 上请求分配内存。当在 TiDB 集群的配置文件中为 TiDB/PD/TiKV 指定numa_node
参数时,即相当于以membind
策略为TiDB
组件进行Numa
绑核。
tidb_servers: - host: 192.168.3.221 ssh_port: 22 port: 4000 status_port: 10080 deploy_dir: /tidb-deploy/tidb-4000 log_dir: /tidb-deploy/tidb-4000/log numa_node: "0,1" arch: amd64 os: linux
--preferred=node
:优先从指定 node 请求内存分配,如果获取失败,则尝试其他 node。--interleave=nodes
或者-i nodes
:规定进程从指定的 nodes 上,以round robin
算法轮询地请求内存分配。
2.4 查看 NUMA 运行信息
可通过 ·numastat· 查看系统中 ·numa node· 的运行信息。
~]$ numastat node0 node1 numa_hit 2428976598 1126105874 numa_miss 1226502 259640186 numa_foreign 259640186 1226502 interleave_hit 23412 23575 local_node 2428960674 1126067489 other_node 1242426 259678571
numa_hit命中的内存,也就是为这个节点成功分配本地内存访问的内存大小。 numa_miss将内存访问分配到另一个 node
节点的内存大小,这个值和另一个 node 的numa_foreign
相对应。如node0
的numa\_miss
与numa\_node1
的numa_foreign
相对应。numa_foreign另一个 Node 访问此 Node 的内存大小,与对方 Node 的 numa_miss
相对应。如node0
的numa_foreign
与numa_node1
的numa_miss
相对应。local_node这个节点的进程,成功在这个节点上分配内存访问的大小。 other_node这个节点的进程,在其它节点上分配的内存访问大小。 很明显,miss 值和 foreign 值越高,就需要考虑 numa 绑定的问题。但是,如果本身内存资源就不足,则可直接禁用 NUMA 特性。
2.5 NUMA 绑核示例
## 将 myapplic 运行在 cpus 0-4、8-12 中 numactl --physcpubind=+0-4,8-12 myapplic arguments ## 将 bigdatabase 运行在所有 cpu node 中,参数为 argments numactl --interleave=all bigdatabase arguments ## 将 process 运行在 node0,并从 node0、node1 申请内存 numactl --cpunodebind=0 --membind=0,1 process ## 将 process 运行在 node0,并从 node0、node1 申请内存 balancing numactl --cpunodebind=0 --balancing --membind=0,1 process ## 将 network-server 运行在 eth0 所属的 node 中,并从该 node 中申请内存 numactl --cpunodebind=netdev:eth0 --membind=netdev:eth0 network-server ## 以 node0 为首选,执行 numactl --show numactl --preferred=0 numactl --show
2.6 禁用 NUMA
针对 Oracle、MySQL 数据库,开启 Numa 可能会带来性能问题,因此建议关闭 Numa 特性。可通过如下方式,关闭 Numa:
通过 BIOS 关闭通过主板 BIOS 禁用 NUMA 特性,不同的主板路径可能不同。 BIOS:interleave = Disable / Enable
通过操作系统关闭 Linux 操作系统,可编辑
/etc/default/grub
文件,在参数GRUB_CMDLINE_LINUX
的值的末尾,追加上numa=off
。以 CentOS7 为例。## 修改 grub 文件 ~]# cat /etc/default/grub GRUB_TIMEOUT=5 GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)" GRUB_DEFAULT=saved GRUB_DISABLE_SUBMENU=true GRUB_TERMINAL_OUTPUT="console" GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=ol/root rd.lvm.lv=ol/swap rhgb quiet numa=off" GRUB_DISABLE_RECOVERY="true" ## 重新生成启动配置 ~]# cp /boot/grub2/grub.cfg /boot/grub2/grub.cfg.bak ~]# cp /boot/efi/EFI/redhat/grub.cfg /boot/efi/EFI/redhat/grub.cfg.bak ~]# grub2-mkconfig -o /boot/grub2/grub.cfg Generating grub configuration file ... Found linux image: /boot/vmlinuz-3.10.0-1127.el7.x86_64 Found initrd image: /boot/initramfs-3.10.0-1127.el7.x86_64.img Found linux image: /boot/vmlinuz-0-rescue-0ffe1c969db740f3bf50f4567ee5fba5 Found initrd image: /boot/initramfs-0-rescue-0ffe1c969db740f3bf50f4567ee5fba5.img ~]# grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg Generating grub configuration file ... Found linux image: /boot/vmlinuz-3.10.0-1127.el7.x86_64 Found initrd image: /boot/initramfs-3.10.0-1127.el7.x86_64.img Found linux image: /boot/vmlinuz-0-rescue-2b736cc499234bc4953ba55d0dd9288c Found initrd image: /boot/initramfs-0-rescue-2b736cc499234bc4953ba55d0dd9288c.img ## 重启系统,确认生效