MySQL在NUMA服务器上需绑定CPU与内存节点、禁用透明大页、确保线程不漂移,否则引发跨节点访问和换页抖动;验证须用numastat和numa_maps,三者缺一不可。

NUMA节点内存不均衡导致MySQL频繁换页
MySQL在多路NUMA服务器上运行时,如果mysqld进程被调度到某个CPU节点,却从远端NUMA节点分配内存(比如innodb_buffer_pool_size过大且未绑定),就会触发跨节点内存访问+周期性换页抖动,vmstat里能看到持续的si/so值跳升,numastat -p $(pgrep mysqld)会显示numa_hit低、numa_miss和numa_foreign高。
- 确认当前mysqld绑定的CPU和内存节点:
numactl --show+cat /proc/$(pgrep mysqld)/status | grep -i "mem" - 禁止MySQL自动跨节点分配内存:启动前加
numactl --cpunodebind=0 --membind=0(假设用Node 0) - 避免
innodb_buffer_pool_size超过单个NUMA节点可用内存——查numastat中各节点Free值,留20%余量 - 不要依赖
mysqld_safe或systemd默认启动方式,它绕过numactl,必须显式封装启动命令
systemd服务里正确配置NUMA亲和性
直接改ExecStart加numactl会失败,因为systemd默认不允许CAP_SYS_NICE能力,且numactl需要root权限才能绑定内存节点。
- 在service文件中启用特权:
CapabilityBoundingSet=CAP_SYS_NICE CAP_SYS_RESOURCE - 用
ExecStartPre预检NUMA拓扑:/usr/bin/bash -c 'numactl --hardware | grep -q "node 0"' - 实际启动写成:
ExecStart=/usr/bin/numactl --cpunodebind=0 --membind=0 /usr/sbin/mysqld $MYSQLD_OPTS ${MYSQLD_ARGS} - 禁用
MemoryAccounting=true(systemd内存统计会干扰NUMA感知,尤其在cgroup v1下)
验证buffer pool是否真落在本地节点
innodb_buffer_pool_dump_now=ON或SHOW ENGINE INNODB STATUS只告诉你缓存命中率,不反映物理内存位置。真正要看的是内核级映射。
- 查进程内存节点分布:
numastat -p $(pgrep mysqld),重点关注Heap和Stack行的numa_hit占比,应>95% - 看具体页帧归属:
sudo cat /proc/$(pgrep mysqld)/numa_maps | awk '$NF ~ /heap/ {print $4}' | sort | uniq -c,输出里应几乎全是interleave:0或N0=,而非N1=或interleave:1 - 若仍看到远端节点分配,检查是否启用了
transparent_hugepage——它会绕过--membind,需在grub里加transparent_hugepage=never
mysqld内部线程与NUMA的隐式冲突
即使主进程绑定了NUMA节点,InnoDB后台线程(如log_writer、page_cleaner)仍可能被内核调度到其他CPU,引发本地内存被远程线程争抢,表现为buffer pool脏页刷写延迟升高。
- 强制所有线程继承主进程NUMA策略:启动时加
numactl --cpunodebind=0 --membind=0 --preferred=0(--preferred兜底防fallback) - 关闭Linux CFS的负载均衡干扰:
echo 0 > /sys/devices/system/node/node0/cpu*/sched_load_balance(仅限调试,生产慎用) - 观察
perf record -e 'sched:sched_migrate_task' -p $(pgrep mysqld),若大量task migrate事件发生在不同node间,说明线程漂移严重
NUMA问题从来不是“绑了就完事”,关键在验证内存真的没跨节点、线程真的没漂移、hugepage真的没捣乱——三者漏一,抖动照旧。










