NUMA架构对服务器性能的影响
现代多路服务器普遍采用NUMA(Non-Uniform Memory Access)架构。以双路Intel Xeon或AMD EPYC服务器为例,每颗CPU有本地内存控制器,访问本地内存延迟约80ns,访问远端内存延迟则升至120-150ns。这个差距在高并发数据库、消息队列等内存密集型场景下会显著影响吞吐量。
通过numactl –hardware可以查看服务器的NUMA拓扑:
$ numactl --hardware
available: 2 nodes (0-1)
node 0 size: 128 GB
node 0 free: 42 GB
node 1 size: 128 GB
node 1 free: 38 GB
distances:
0 1
0 10 21
1 21 10
distance矩阵中,10表示本地访问,21表示远端访问,数值越大延迟越高。双路系统差距约2倍,四路系统远端延迟可达3倍以上。
识别跨NUMA节点的内存访问
Linux内核提供了numastat工具统计每个NUMA节点的内存分配情况:
$ numastat -p mysqld
Per-node process memory usage (in MBs) for PID 1234 (mysqld)
Node 0 Node 1 Total
--------------- --------------- ---------------
Huge 0 0 0
Heap 832 456 1288
Stack 4 2 6
Private 128 64 192
Total 964 522 1486
如果Heap在两个节点上分布不均匀,且总内存远超单节点容量,说明进程大量访问远端内存。此时可以通过perf进一步确认:
$ perf stat -e numastat_node0,numastat_node1 -p 1234 sleep 10
Performance counter stats for process id 1234:
1,234,567 numastat_node0 (本地节点命中)
876,543 numastat_node1 (远端节点访问)
2.11 seconds time elapsed
远端访问占比超过20%时,就需要考虑NUMA调优。
内存分配策略调优
Linux默认的NUMA内存分配策略是preferred(优先本地),但当本地内存不足时会自动分配远端内存。通过numactl可以精确控制:
# 绑定到NUMA节点0运行,仅在本地分配内存
numactl --cpunodebind=0 --membind=0 ./my_app
# 交错分配内存,适合内存均匀访问的场景
numactl --interleave=all ./my_app
# 优先本地分配,不足时允许远端(比默认更激进)
numactl --preferred=0 ./my_app
不同策略适用场景:
– –membind:数据库、缓存服务,访问模式集中,严格限制在本地节点。
– –interleave:科学计算、批量处理,内存访问模式均匀,追求整体带宽。
– –preferred:Web应用、混合负载,兼顾本地优先和弹性分配。
中断绑核优化网络性能
网卡中断默认由内核调度到任意CPU处理,在NUMA架构下会导致中断处理和用户态数据分处不同节点,增加内存访问延迟。将网卡中断绑定到与用户进程相同的NUMA节点,可以显著降低延迟。
首先确认网卡所在的NUMA节点:
$ cat /sys/class/net/eth0/device/numa_node
0
# 如果返回-1,说明内核未正确识别NUMA亲和性,需手动设置
然后使用irqbalance或手动设置中断亲和性:
# 停止irqbalance服务
systemctl stop irqbalance
# 查看网卡中断号
$ grep eth0 /proc/interrupts
45: 12345678 IO-APIC 45 eth0-TxRx-0
46: 8765432 IO-APIC 46 eth0-TxRx-1
47: 5432109 IO-APIC 47 eth0-TxRx-2
# 将中断绑定到NUMA节点0的CPU(假设为0-15,32-47)
echo 0xffff0000ffff > /proc/irq/45/smp_affinity
echo 0xffff0000ffff > /proc/irq/46/smp_affinity
echo 0xffff0000ffff > /proc/irq/47/smp_affinity
对于支持多队列的网卡(如Intel XL710),每个队列对应一个中断,建议将不同队列均匀绑定到NUMA节点的各个核心上,避免中断集中导致单核瓶颈。
KVM虚拟机的NUMA透传配置
在虚拟化环境中,Guest OS感知不到物理NUMA拓扑,可能导致vCPU跨节点调度。Libvirt支持NUMA透传:
<domain type='kvm'>
<cpu mode='host-passthrough'>
<topology sockets='2' dies='1' cores='16' threads='2'/>
<numa>
<cell id='0' cpus='0-31' memory='64' unit='GiB' memAccess='shared'/>
<cell id='1' cpus='32-63' memory='64' unit='GiB' memAccess='shared'/>
</numa>
</cpu>
<numatune>
<memory mode='strict' nodeset='0,1'/>
<memnode cellid='0' mode='strict' nodeset='0'/>
<memnode cellid='1' mode='strict' nodeset='1'/>
</numatune>
</domain>
numatune的strict模式确保vCPU的内存分配严格绑定到物理NUMA节点,避免跨节点访问。配合CPU的host-passthrough模式,Guest OS可以直接感知NUMA拓扑并做应用级优化。
内核参数调优建议
以下内核参数与NUMA行为相关,建议根据场景调整:
# 禁用内核的自动NUMA平衡(某些场景下反而增加开销)
kernel.numa_balancing = 0
# 大页内存分配到指定NUMA节点
echo 1024 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages
# 调整vm.zone_reclaim_mode(1=本地内存回收优先)
vm.zone_reclaim_mode = 1
kernel.numa_balancing默认开启,内核会自动迁移进程内存到当前运行CPU所在的NUMA节点。对于绑定了CPU的应用(如数据库),这个机制会造成不必要的页面迁移开销,建议关闭。对于未绑定CPU的通用负载,保持开启可以降低远端访问概率。
NUMA调优不是一次性的工作,需要在部署前做好拓扑规划,在运行中持续监控numastat和perf指标,根据负载变化动态调整绑定策略。核心原则很简单:让数据和处理它的CPU在同一个NUMA节点上。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/linux-fu-wu-qi-numa-jia-gou-xing-neng-diao-you-cong-nei-cun/