Linux NUMA架构感知配置与CPU亲和性绑定性能调优实战

多路服务器普遍采用NUMA(Non-Uniform Memory Access)架构,每颗CPU拥有本地内存控制器,访问本地内存延迟约80ns,跨CPU访问远端内存延迟可达150ns以上。在高性能计算和数据库场景中,忽视NUMA拓扑会导致性能下降20%至40%。Linux内核提供numactl、cgroup和systemd等多层工具进行NUMA感知配置,合理利用这些工具是服务器性能调优的关键环节。

NUMA拓扑结构识别与信息查看

主流双路服务器通常有2个NUMA节点,四路服务器有4个。每个NUMA节点包含一组CPU核心和本地内存。查看NUMA拓扑的常用命令:

# 查看NUMA节点数量和CPU分布
numactl --hardware
# 输出示例(双路EPYC 9654):
# available: 2 nodes (0-1)
# node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 ... 59 60 61 62 63
# node 1 cpus: 64 65 66 ... 127
# node 0 size: 257735 MB
# node 1 size: 257734 MB
# node 0 free: 245000 MB
# node 1 free: 246000 MB

# 查看NUMA距离矩阵
numactl --hardware | grep distance
# node 0 distance: 10 20
# node 1 distance: 20 10
# 本地访问距离10,跨节点距离20

# 查看PCI设备的NUMA归属
cat /sys/bus/pci/devices/0000:01:00.0/numa_node
# 输出0表示该设备属于NUMA节点0

# 查看网卡NUMA归属(对网络性能优化重要)
for irq in $(grep eth0 /proc/interrupts | awk '{print $1}' | tr -d :); do
    echo "IRQ $irq: $(cat /proc/irq/$irq/node)"
done

# 使用lscpu查看缓存拓扑
lscpu --cache
# L1d cache: 32K
# L1i cache: 32K
# L2 cache: 1024K
# L3 cache: 32768K  (共享范围标注NUMA节点)

NUMA距离是相对值,本地节点设为10。距离20意味着跨节点访问延迟是本地的2倍。对于网卡、GPU等PCIe设备,其DMA操作直接通过所在NUMA节点的内存控制器完成。如果应用运行在NUMA节点0但网卡在NUMA节点1,数据需跨节点传输,网络延迟显著增加。

numactl内存绑定策略

numactl是最基础的NUMA控制工具,提供四种内存分配策略:

# 1. --membind: 只允许从指定节点分配内存
numactl --membind=0 ./database_server
# 进程只能使用节点0的内存,即使节点0内存不足也不会使用节点1

# 2. --preferred: 优先从指定节点分配,不足时回退到其他节点
numactl --preferred=1 ./application
# 尽量使用节点1内存,不够时自动回退

# 3. --interleave: 交替从所有节点分配内存(轮询)
numactl --interleave=all ./memory_intensive_app
# 适用于内存访问模式无法预测的场景
# 大页内存配合interleave效果更佳
numactl --interleave=all --hugetlbfs.shm=yes ./app

# 4. --cpunodebind + --membind: CPU和内存同时绑定
numactl --cpunodebind=0 --membind=0 ./critical_service
# 进程的CPU和内存都限定在节点0,零跨节点访问

# 查看进程当前NUMA内存分布
numastat -p $(pidof database_server)
# 输出示例:
# Per-node process memory usage (in MBs)
# PID     Node 0 Node 1 Total
# 12345   8192   2048   10240
# 说明该进程大部分内存在节点0,少量在节点1

CPU亲和性绑定与中断分配

CPU亲和性(CPU Affinity)控制进程在哪些CPU核心上运行。set-affinity可减少缓存失效和NUMA跨节点访问。Linux提供taskset和sched_setaffinity两种方式:

# taskset命令行工具
# 查看进程CPU亲和性
taskset -p $(pidof mysqld)
# pid 12345's current affinity mask: ffffffffffffffff

# 绑定到节点0的CPU(0-63)
taskset -c 0-63 -p $(pidof mysqld)
# pid 12345's current affinity mask: ffffffffffffffff
# pid 12345's new affinity mask: 00000000ffffffff

# 启动时绑定
taskset -c 0-63 mysqld --defaults-file=/etc/my.cnf

# 使用cgroup v2设置CPU亲和性
mkdir -p /sys/fs/cgroup/db_service
echo "0-63" > /sys/fs/cgroup/db_service/cpuset.cpus
echo "0" > /sys/fs/cgroup/db_service/cpuset.mems
echo $(pidof mysqld) > /sys/fs/cgroup/db_service/cgroup.procs

# systemd服务单元配置
# /etc/systemd/system/database.service
[Service]
CPUAffinity=0-63
NUMAPolicy=bind
NUMAMask=0
ExecStart=/usr/bin/mysqld --defaults-file=/etc/my.cnf

网卡中断的NUMA绑定对网络密集型应用至关重要。默认情况下,网卡中断可能集中在某个CPU核心上,成为瓶颈。通过smp_affinity将中断分散到不同核心,并与应用CPU绑定保持NUMA一致性:

# 查看网卡中断号
grep eth0 /proc/interrupts
# 输出:
#  51:  1045321  0  0  0  ...  IR-PCI-MSI  0000:01:00.0  eth0-rx-0
#  52:  987654   0  0  0  ...  IR-PCI-MSI  0000:01:00.0  eth0-tx-0

# 设置RX队列中断的CPU亲和性
# 假设网卡在NUMA 0,将RX中断绑定到节点0的核心
echo $(printf "%x" $((1 << 0))) > /proc/irq/51/smp_affinity  # 绑定到CPU0
echo $(printf "%x" $((1 << 1))) > /proc/irq/52/smp_affinity  # 绑定到CPU1

# 多队列网卡的RPS(Receive Packet Steering)配置
# 将软中断分散到节点0的所有核心
echo "00000000ffffffff" > /sys/class/net/eth0/queues/rx-0/rps_cpus

# 开启RFS(Receive Flow Steering),按流哈希分发
echo 32768 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
echo 32768 > /proc/sys/net/core/rps_sock_flow_entries

数据库与中间件NUMA调优实践

MySQL的InnoDB Buffer Pool是最大的内存消费者。在双路NUMA服务器上,默认行为是跨节点交替分配内存,导致Buffer Pool中约50%的page在远端节点。优化方案是将MySQL实例绑定到单个NUMA节点,或使用interleave策略:

# 方案1: 每个NUMA节点运行一个MySQL实例
# 节点0上的MySQL实例
numactl --cpunodebind=0 --membind=0 mysqld \
    --defaults-file=/etc/my.cnf.node0 \
    --innodb-buffer-pool-size=200G

# 节点1上的MySQL实例
numactl --cpunodebind=1 --membind=1 mysqld \
    --defaults-file=/etc/my.cnf.node1 \
    --innodb-buffer-pool-size=200G

# 方案2: 单实例interleave分配
numactl --interleave=all mysqld \
    --innodb-buffer-pool-size=400G \
    --innodb-buffer-pool-instances=16

# Redis NUMA优化
# Redis是单线程模型,绑定到单个NUMA节点效果最佳
numactl --cpunodebind=0 --membind=0 redis-server \
    /etc/redis/redis.conf --maxmemory 64gb

# Java应用NUMA调优
# JVM通过-XX:+UseNUMA感知NUMA拓扑
java -XX:+UseNUMA -Xms200g -Xmx200g \
    -XX:+UseG1GC -jar application.jar

NUMA性能监控与验证

使用numastat监控NUMA级别的内存访问统计:

# 系统级NUMA统计
numastat
# 输出各节点的:
# numa_miss   - 本应本地分配但分配到远端的次数
# numa_foreign  - 远端分配到本地的次数
# numa_interleave - interleave分配的页数

# 按进程查看NUMA内存分布
numastat -p $(pidof mysqld) $(pidof redis-server)

# 硬件性能计数器监控跨节点访问
# 使用perf监控NUMA相关事件
perf stat -e cache-misses,cache-references,mem_load_l3_miss_retired.local_dram,mem_load_l3_miss_retired.remote_dram ./application

# 输出示例:
# cache-misses          1,234,567,890
# cache-references      5,678,901,234
# local_dram accesses   890,123,456
# remote_dram accesses  456,789,012
# remote占比 = 456789012 / (890123456 + 456789012) = 33.9%

# 理想状态下remote_dram占比应低于10%

算力资源规划中,NUMA感知是物理机架设的基本功。云服务器选型时,裸金属实例直接暴露NUMA拓扑可做精细调优,而虚拟化实例的vCPU与NUMA节点的映射关系不可控,需要在虚拟化层配置CPU pinning。IDC数据中心的服务器高可用集群部署中,跨节点的内存访问延迟差异会直接影响分布式锁和共识算法的延迟,在Raft/Paxos等一致性协议的超时配置中需要纳入考量。服务器故障排查时,numastat中的numa_miss突增往往指示着cgroup配置错误或内存迁移导致的性能抖动。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/linuxnuma-jia-gou-gan-zhi-pei-zhi-yu-cpu-qin-he-xing-bang/

(0)
小编小编
上一篇 6小时前
下一篇 6小时前

相关推荐