多路服务器普遍采用NUMA(Non-Uniform Memory Access)架构,各CPU节点拥有本地内存,跨节点访问延迟显著高于本地访问。在服务器运维和高性能计算场景中,NUMA感知配置对数据库、大模型推理、虚拟化等内存密集型应用性能影响可达20%-40%。本文从架构原理到实操配置,系统讲解NUMA性能调优方法。
NUMA架构原理与多物理CPU拓扑结构
UMA架构中所有CPU通过同一总线访问统一内存池,延迟一致。当CPU核心数扩展到数十甚至上百时,总线带宽成为瓶颈。NUMA架构将CPU和内存划分为多个Node,每个Node拥有本地内存控制器,CPU访问本地内存延迟最低,访问远端Node内存需经过interconnect总线(Intel QPI/UPI或AMD Infinity Fabric),延迟增加30%-100%。
查看服务器NUMA拓扑:
# 查看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 1 cpus: 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
node 1 size: 65536 MB
lscpu命令可查看更详细的拓扑信息,包括各Node的CPU列表和内存分布:
lscpu | grep NUMA
# NUMA node(s): 2
# NUMA node0 CPU(s): 0-15
# NUMA node1 CPU(s): 16-31
numactl工具内存绑定策略与CPU亲和性配置
numactl是Linux提供的NUMA策略控制工具,支持以下内存分配模式:
– –membind=nodes:仅从指定Node分配内存
– –cpunodebind=nodes:仅在指定Node的CPU上运行
– –preferred=node:优先从指定Node分配内存,不足时允许跨节点
– –interleave=nodes:交错分配内存,均匀分布在各Node
对内存密集型应用,绑定到单个NUMA Node消除跨节点访问:
# 将进程绑定到Node 0的CPU和内存
numactl --cpunodebind=0 --membind=0 ./my_application
# 对多实例部署,各实例绑定不同Node
numactl --cpunodebind=0 --membind=0 ./instance_1 &
numactl --cpunodebind=1 --membind=1 ./instance_2 &
对于已运行的进程,使用taskset调整CPU亲和性:
# 查看进程当前CPU亲和性
taskset -cp <PID>
# 绑定到Node 0的CPU(0-15)
taskset -cp 0-15 <PID>
NUMA Balance自动迁移机制与性能影响
Linux内核默认开启NUMA Balancing,会在运行时自动将进程内存页迁移到其运行CPU所在的NUMA Node。对于大多数通用工作负载,这一机制能改善内存局部性。但对于数据库等大内存应用,频繁的页面迁移会导致性能抖动。
查看和关闭NUMA Balancing:
# 查看当前状态
cat /proc/sys/kernel/numa_balancing
# 1 表示开启
# 临时关闭
echo 0 > /proc/sys/kernel/numa_balancing
# 永久关闭(写入sysctl.conf)
echo "kernel.numa_balancing = 0" >> /etc/sysctl.conf
sysctl -p
关闭NUMA Balancing后,需手动通过numactl确保进程的内存局部性。对Redis、MySQL等单进程大内存服务,关闭自动迁移+手动绑定效果更稳定。
多线程应用NUMA感知优化与内存局部性
多线程应用中,线程的CPU亲和性和内存分配需协同配置。线程应优先访问其运行CPU所在Node的本地内存。以C++程序为例,使用pthread API绑定线程到特定NUMA Node:
#define _GNU_SOURCE
#include <pthread.h>
#include <sched.h>
#include <numa.h>
void* worker_thread(void* arg) {
int node_id = *(int*)arg;
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
// 绑定到指定Node的CPU核心
struct bitmask* cpus = numa_allocate_cpumask();
numa_node_to_cpus(node_id, cpus);
for (int i = 0; i < cpus->size; i++) {
if (numa_bitmask_isbitset(cpus, i)) {
CPU_SET(i, &cpuset);
break;
}
}
pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
// 在本地Node分配内存
void* buffer = numa_alloc_onnode(1024 * 1024, node_id);
// ... 工作逻辑
numa_free(buffer, 1024 * 1024);
return NULL;
}
Java应用通过JVM参数实现NUMA感知。JDK 8+支持-XX:+UseNUMA参数,使G1 GC在分配Region时优先使用本地Node内存:
java -XX:+UseNUMA -XX:+UseG1GC -Xmx32g -jar application.jar
NUMA架构下数据库性能调优实践
MySQL InnoDB引擎的缓冲池默认从所有NUMA Node交错分配内存。在numa_balancing关闭的环境下,可能出现缓冲池集中在单个Node导致跨节点访问的问题。
推荐配置方案:
# 使用numactl --interleave启动MySQL
numactl --interleave=all mysqld --defaults-file=/etc/my.cnf
# 或在systemd服务文件中配置
[Service]
ExecStart=/usr/bin/numactl --interleave=all /usr/sbin/mysqld
PostgreSQL推荐使用numactl –membind绑定到内存较大的Node,或使用–interleave均衡分布。对于Redis,单实例绑定到单个Node效果最佳,多实例部署时各实例分别绑定不同Node。
验证NUMA内存分布是否均衡:
# 查看各Node内存使用情况
numastat -p <PID>
# 输出会显示各Node的Total内存、Local内存和Foreign内存比例
# Foreign内存比例应低于10%,否则需调整绑定策略
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/fu-wu-qi-numa-jia-gou-nei-cun-qin-he-xing-bang-ding-yu-xing/