Linux服务器在多路CPU架构下,NUMA(Non-Uniform Memory Access)拓扑对应用性能的影响常被忽视。现代数据中心服务器普遍采用双路或四路CPU配置,每颗CPU拥有本地内存控制器,跨NUMA节点访问内存的延迟比本地访问高出50%-100%。数据库、消息队列、AI推理等内存密集型应用在NUMA配置不当的情况下,实际吞吐量可能下降30%以上。服务器运维人员需要掌握NUMA拓扑识别、CPU亲和性绑定和内存策略配置,才能在硬件层面释放多路服务器的完整算力。
NUMA架构原理与拓扑识别
NUMA架构的核心问题是内存访问的非一致性。以一台双路Intel Xeon Platinum服务器为例,CPU 0(Node 0)访问本地内存延迟约80ns,访问CPU 1(Node 1)的远程内存延迟约130-150ns。在四路服务器上,最远端NUMA节点的内存访问延迟可达本地访问的2.5倍。
# 查看NUMA拓扑结构
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
node 0 size: 65536 MB
node 0 free: 52000 MB
node 1 cpus: 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
node 1 size: 65536 MB
node 1 free: 51000 MB
node distances:
node 0 1
0: 10 21
1: 21 10
# 查看进程的NUMA内存分布
numastat -p $(pgrep -f mysqld)
# 查看PCI设备的NUMA亲和性
cat /sys/bus/pci/devices/0000:81:00.0/numa_node
# 输出: 1 表示该网卡属于NUMA Node 1
node distances矩阵中,10表示本地访问基准值,21表示跨节点访问的相对延迟倍数。21/10 = 2.1,意味着跨节点访问延迟约为本地的2.1倍。这个数值直接影响网络IO密集型应用的性能——如果网卡在Node 1,而处理网络中断的CPU核心在Node 0,每个网络数据包都要跨节点传输,延迟和带宽都会受影响。
numactl工具与内存策略配置
numactl是Linux下控制NUMA内存分配策略的核心工具,支持四种内存绑定模式:–localalloc(优先从当前CPU所在节点分配内存)、–membind(强制从指定节点分配)、–preferred(优先从指定节点分配,不足时回退到其他节点)、–interleave(轮询在所有节点间分配内存)。
# 方式一:命令行直接绑定
# 将MySQL绑定到Node 0,内存仅从Node 0分配
numactl --cpunodebind=0 --membind=0 /usr/sbin/mysqld --defaults-file=/etc/my.cnf
# Redis绑定到Node 1
numactl --cpunodebind=1 --membind=1 redis-server /etc/redis/redis.conf
# 方式二:interleave模式(适用于内存访问模式不确定的场景)
numactl --interleave=all java -jar application.jar -Xmx32g
# 方式三:systemd服务NUMA绑定
# /etc/systemd/system/mysqld.service.d/numa.conf
[Service]
NUMAPolicy=bind
NUMANode=0
CPUAffinity=0-15
# 方式四:确认绑定是否生效
cat /proc/$(pgrep mysqld)/status | grep -i "cpus_allowed"
cat /proc/$(pgrep mysqld)/numa_maps | head -5
interleave模式适用于两类场景:一是应用的内存访问模式高度随机(如某些图数据库),无法预知哪部分数据会被频繁访问;二是应用的内存需求超过单个NUMA节点的物理内存,需要跨节点分配。interleave模式下,内存页在所有节点间均匀分布,避免了单节点内存耗尽,但牺牲了局部性优势。对于Redis这类纯内存缓存服务,bind模式几乎总是优于interleave——Redis的内存访问模式虽然不完全确定,但整体热点集中在相对较小的键空间内,绑定到单节点能保证缓存命中时的本地内存访问。
CPU亲和性绑定:taskset与cgroup方案
CPU亲和性绑定确保进程只在指定的CPU核心上运行,避免操作系统调度器将进程迁移到其他核心导致缓存失效。taskset适用于简单的CPU绑定场景,cgroup v2则提供更细粒度的资源隔离控制。
# taskset绑定进程到指定CPU核心
taskset -c 0-7 /opt/app/bin/worker
# 对已运行进程设置亲和性
taskset -cp 0-7 $(pgrep -f worker)
# 查看当前进程的CPU亲和性
taskset -cp $(pgrep -f worker)
# 输出: pid 12345's current affinity list: 0-7
# cgroup v2 NUMA感知配置
# /sys/fs/cgroup/app.slice/cpuset.cpus
echo "0-15" > /sys/fs/cgroup/app.slice/cpuset.cpus
echo "0" > /sys/fs/cgroup/app.slice/cpuset.mems
# 网卡中断绑定:将网卡中断分配到网卡所在NUMA节点的CPU
# 查看网卡所属NUMA节点
cat /sys/class/net/eth0/device/numa_node
# 绑定网卡队列中断
for i in $(grep eth0 /proc/interrupts | awk -F: '{print $1}' | tr -d ' '); do
echo $((i % 16)) > /proc/irq/$i/smp_affinity_list
done
网卡中断绑定是NUMA调优中收益最明显的操作之一。默认情况下,Linux的IRQ亲和性由irqbalance守护进程动态调整,它倾向于将中断分散到所有CPU核心以实现负载均衡,但忽略了NUMA拓扑。对于万兆以上网卡,手动将RX/TX队列中断绑定到网卡所在NUMA节点的CPU核心,可以减少跨节点DMA传输,实测在40G网卡场景下,吞吐量提升约15%,p99延迟降低约20%。
数据库与中间件NUMA调优实践
不同应用对NUMA的敏感度差异显著。内存访问密集型应用(MySQL InnoDB、Redis、Memcached)对NUMA配置极为敏感,计算密集型应用(AI推理)的影响相对较小但仍然显著。
# MySQL NUMA调优配置
# my.cnf 中添加
[mysqld]
# innodb_buffer_pool_size 应不超过单NUMA节点内存的80%
innodb_buffer_pool_size = 48G # Node 0有64G内存,预留20%给系统
innodb_numa_interleave = 1 # MySQL 8.0+内置NUMA感知选项
# PostgreSQL NUMA调优
# 使用hugepages减少TLB miss
sysctl -w vm.nr_hugepages = 20480
# postgresql.conf
shared_buffers = 32GB
huge_pages = try
# Kafka NUMA调优
# 将broker的网络线程和IO线程绑定到不同NUMA节点
# server.properties
num.network.threads = 8 # 绑定到网卡所在节点
num.io.threads = 16 # 绑定到另一个节点
# 启动脚本中分别绑定
numactl --cpunodebind=0 --membind=0 java -cp ... kafka.network.SocketServer &
numactl --cpunodebind=1 --membind=1 java -cp ... kafka.log.LogManager &
NUMA性能基准测试与对比
调优效果需要通过基准测试量化验证。常用的测试工具包括sysbench(数据库压测)、redis-benchmark(缓存压测)、iperf3(网络吞吐测试)。以下是双路Xeon服务器上MySQL NUMA调优前后的对比数据:
# sysbench压测命令
sysbench oltp_read_write --mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=root --mysql-password=xxx --mysql-db=sbtest --table-size=10000000 --threads=32 --time=300 run
# 调优前(默认NUMA策略,进程跨节点调度)
# QPS: 28,500
# P99延迟: 45ms
# CPU利用率: 65%(大量时间消耗在跨节点内存访问)
# 调优后(bind到Node 0,CPUAffinity=0-15)
# QPS: 41,200 (+44.6%)
# P99延迟: 22ms (-51.1%)
# CPU利用率: 85%(内存访问效率提升)
# 使用perf验证内存访问模式
perf stat -e cache-misses,cache-references,mem_load_l3_miss_retired.local_dram,mem_load_l3_miss_retired.remote_dram -p $(pgrep mysqld) -- sleep 30
# 调优前 remote_dram/local_dram 比值: 1.8
# 调优后 remote_dram/local_dram 比值: 0.05
remote_dram/local_dram比值的下降直接反映了跨NUMA节点内存访问的减少。当该比值趋近于0时,说明几乎所有内存访问都在本地NUMA节点完成,NUMA调优已达到理想状态。对于运行多实例的场景,建议将不同实例分别绑定到不同NUMA节点,既能避免跨节点访问,又能充分利用所有CPU资源。每台双路服务器运行两个MySQL实例,分别绑定到Node 0和Node 1,总吞吐量通常优于单实例跨节点运行。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/linux-fu-wu-qi-numa-jia-gou-xing-neng-diao-you-shi-zhan-cpu/