物理内存充足却频繁使用Swap,根源在于NUMA不均衡、InnoDB缓存配置越界、OS缓存争抢及内核换出策略失当;需同步启用innodb_numa_interleave、下调innodb_buffer_pool_size、配置O_DIRECT、设置memlock锁页。

物理内存充足却频繁使用 Swap,不是系统“乱来”,而是内核在 NUMA 不均衡、内存回收策略失当或 MySQL 自身缓存配置越界时的被动响应。关键不在“禁用 Swap”,而在让 mysqld 的内存真正驻留在 RAM 中、不被换出。
确认是不是 NUMA 导致的假性内存不足
多路 CPU 服务器(尤其 Intel Xeon + CentOS/RHEL)开启 NUMA 后,MySQL 默认只从本地 node 分配内存。若 node 0 内存快满而 node 1 还空闲,内核会认为“整体缺内存”,触发 swap——哪怕 free -h 显示 available 还有 10G+。
- 运行 numastat -p $(pgrep mysqld),看各 node 的 numa_hit / numa_miss 差距是否极大
- 若 mysqld 主要跑在 node 0,但 node 1 的 free 大量闲置,就是典型 NUMA 偏斜
- MySQL 5.6.27+:在 my.cnf 的 [mysqld] 段加 innodb_numa_interleave = ON
- 旧版本或需兼容启动脚本:修改 /usr/bin/mysqld_safe,在启动命令前加 numactl --interleave=all
压住 innodb_buffer_pool_size,别让它“假装能用”
设成物理内存的 70% 或 80%,只是理论上限。实际中,buffer pool 会和 OS cache、redo log buffer、连接线程堆栈等争抢——一旦总 RSS 超过 free -h 输出的 available 值,swap 就开始悄悄工作。
- 把 innodb_buffer_pool_size 设为物理内存的 50%(例如 32G 机器设 16G),单位写 16G,别写 16384M
- 关掉两个默认吃内存行为:innodb_buffer_pool_dump_at_shutdown = OFF 和 innodb_buffer_pool_load_at_startup = OFF
- 检查当前真实 RSS:ps -o pid,rss,comm -C mysqld,确保数值远低于 available(留至少 2G 余量)
绕过 OS 缓存争抢,减少 page reclaim 压力
innodb_flush_method=O_DIRECT 不是“可选项”,而是 NUMA + 高写入场景下的刚需。它让 .ibd 文件读写跳过 page cache,避免 buffer pool 和 OS cache 双重缓存同一份数据,大幅降低内存回收频率。
- my.cnf 中明确写:innodb_flush_method = O_DIRECT(不能拼错,不能混用 O_DSYNC)
- 启用后,innodb_buffer_pool_size 必须同步下调(比如原来设 80%,现在压到 ≤70%),否则 mysqld 启动失败
- 验证生效:mysql -e "SHOW VARIABLES LIKE 'innodb_flush_method';",输出必须是 O_DIRECT
用 memlock 锁住关键内存页,不让内核动它
swappiness=1 只是降低倾向,不能阻止换出;memlock 才是让 mysqld 的匿名页(如 buffer pool)彻底免疫 swap 的硬手段。
- 在 /etc/security/limits.conf 末尾加两行:
mysql soft memlock unlimited
mysql hard memlock unlimited - my.cnf 的 [mysqld] 段加:innodb_use_sys_malloc = 0(配合 jemalloc 时必须关系统 malloc)
- 确保 mysqld 以 mysql 用户身份由 root 启动(systemd 下检查 LimitMEMLOCK 是否设为 infinity)
- 验证:cat /proc/$(pgrep mysqld)/status | grep -i cap,CapBnd 字段应含 cap_ipc_lock
不复杂但容易忽略:Swap 高从来不是单一参数问题,而是 NUMA 分配、buffer pool 实际占用、OS 缓存策略、内核锁页能力四者共同作用的结果。调一个,不如四点齐抓。


















