暂无图片
暂无图片
暂无图片
暂无图片

关于 NUMA 绑核,你需要了解的。。。

OnTheRoad 2022-09-13
2245

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 厂商将内存控制器芯片集成到 CPU 内部,一般一个 CPU socket 会有一个独立的内存控制器
  • 每个 CPU scoket 独立直连到一部分内存,这部分直连内存称为此 CPU 的 “本地内存”。
  • 每个 CPU socket 与其直连的 “本地内存”,可称为一个 “ Numa Node ”。
  • 多个 CPU 之间通过 QPI( Quick Path Interconnect )总线进行连接。CPU 可以通过 QPI 总线访问不与自己直连的 “ 远程内存 ”。
  • Numa Node 内的 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
    ## 重启系统,确认生效