NUMA(Non-Uniform Memory Access)是多路服务器的默认内存架构。双路EPYC 9004系列CPU每颗处理器有自己的本地DDR5内存控制器,本地内存访问延迟约80ns,跨NUMA节点访问要走Infinity Fabric互联总线,延迟升至130ns以上,带宽减半。数据库、消息队列这类内存密集型应用在NUMA配置不当的场景下,性能损失20%到50%的情况并不少见,问题往往出在线程与内存的节点绑定策略上。
NUMA拓扑信息查看与lscpu命令输出解读
动手调优前需要先确认机器的真实拓扑。lscpu命令输出里的NUMA相关字段:
lscpu | grep -i numa
# NUMA node(s): 2
# NUMA node0 CPU(s): 0-63,128-191
# NUMA node1 CPU(s): 64-127,192-255
# NUMA node0 memory: 0-1023G
# NUMA node1 memory: 1024-2047G
每个物理核在两个NUMA节点各有一组逻辑核(超线程),编号被切开是BIOS把超线程对分别映射到不同节点的结果。numactl –hardware能列出每个节点的内存总量、空闲量与节点距离:
numactl -H
# available: 2 nodes (0-1)
# node 0 cpus: 0 1 2 ... 63
# node 0 size: 102749 MB
# node 0 free: 98021 MB
# node 1 size: 102748 MB
# node 1 free: 99012 MB
# distances:
# node 0 1
# 0: 10 20
# 1: 20 10
距离值10是本地,20是远端,这个比值越大说明跨节点代价越高。用numastat命令看应用是否发生了跨节点分配:
numastat -p $(pidof mysqld)
# Per-node process memory usage (in MBs)
# Node 0 Node 1 Total
# Heap: 8123 214 8337
# Foreign: # 跨节点统计
# numa_hit: 9123000 1203000
# numa_miss: 231000 1450000 # miss越高说明内存分配错节点越多
numa_miss远高于本地命中率时,就是跨节点访问拖慢了应用,需要干预内存分配策略。
numactl绑定策略与内存分配模式配置
numactl是调整进程NUMA行为最直接的工具,四个常用参数覆盖了绝大多数场景:
# 场景1:进程与内存全部绑在节点0(数据库单实例常用)
numactl --cpunodebind=0 --membind=0 /usr/local/mysql/bin/mysqld
# 场景2:CPU允许全部节点,内存优先本地分配,不足时允许远端
numactl --interleave=all ./app
# 场景3:只绑定内存节点,CPU浮动
numactl --membind=1 ./app
# 场景4:交叉分配内存,均摊到所有节点(大内存应用防热点)
numactl --interleave=all java -Xms64g -Xmx64g -jar app.jar
参数选择的原则:CPU密集型应用用cpunodebind加membind成对绑定,保证线程与数据同节点;内存占用超过单节点容量的应用用interleave=all,否则后半段内存必然跨节点,不如均摊;网卡中断处理要注意,IRQ亲和性若绑在节点0而收包缓冲区(skb)在节点1分配,每个数据包都产生跨节点拷贝,用ethtool配合irqbalance把网卡队列中断绑到接收进程所在节点。
Kubernetes与数据库场景的NUMA对齐实践
容器化环境里NUMA问题更隐蔽。kubelet的静态CPU管理策略(static policy)配合CPU Manager能保证独占CPU池,但内存与设备对齐需要Topology Manager。kubelet配置示例:
# /var/lib/kubelet/config.yaml
cpuManagerPolicy: static
topologyManagerPolicy: single-numa-node
reservedSystemCPUs: 0,1
# 加上SR-IOV/dpdk等插件时设备亲和性一起对齐
topologyManagerPolicy设为single-numa-node后,Pod请求的CPU、内存与SR-IOV网卡会被调度到同一NUMA节点,NFV与低延迟交易类负载必须开启这一项。策略取值从严到松依次是none、best-effort、restricted、single-numa-node,后两者会在资源无法满足时拒绝调度,生产上用restricted起步观察Admit失败率再收紧。
数据库场景的实测参考:MySQL 8.0在双路服务器上跑只读QPS压测,进程未绑定时numa_miss占比31%,QPS约18万;用numactl –cpunodebind=0 –membind=0绑定到单节点(牺牲一半CPU核心数)后QPS反而升到24万,本地命中率达98%。原因是InnoDB缓冲池热点页访问延迟下降,跨节点一致性流量消失。Redis这类单线程内存敏感服务更明显,绑定正确节点后P99延迟下降可达两成。调优完成后把numactl写入systemd服务单元的ExecStart,避免重启后策略丢失:
# /etc/systemd/system/redis.service 片段
ExecStart=/usr/bin/numactl --cpunodebind=1 --membind=1 /usr/bin/redis-server
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/fu-wu-qi-numa-jia-gou-diao-you-shi-zhan-kua-jie-dian-nei/