mysql卡但cpu低时,大概率是swap导致:先用vmstat 1查si/so是否持续大于0,再结合free -h确认swapused上涨;需设innodb_flush_method=o_direct、swappiness=1、memlock无限并启用innodb_numa_interleave。

MySQL卡但CPU很低,先看 vmstat 1 里的 si 和 so
这不是“可能”,而是大概率就是SWAP在拖垮MySQL:mysqld 进程%CPU只有个位数,但 SELECT 1 都超时、连接堆积、慢查询日志里全是“Waiting for table flush”——这时候别急着调索引或查锁,vmstat 1 看一眼 si(swap-in)和 so(swap-out)是否持续大于 0,再配合 free -h 看 SwapUsed 是否上涨。如果 swpd > 0 且 si/so 不归零,基本可锁定是内存换入换出导致的延迟毛刺。
注意:used 内存高 ≠ 危险;available 持续低于 1G + swpd 上涨,才是真危险信号。
innodb_flush_method=O_DIRECT 要配对生效,不能只改一半
设了 innodb_flush_method=O_DIRECT 却没效果?常见原因是没关掉OS缓存争抢——它只对 InnoDB 数据文件(.ibd)绕过 page cache,但 redo log 默认仍走 OS cache。这会导致 buffer pool 和 log buffer 在内存里反复竞争,尤其在写密集场景下加剧 page reclaim,间接推高 SWAP 倾向。
- 必须确认
my.cnf中[mysqld]段已写入:innodb_flush_method=O_DIRECT - 不要和
innodb_flush_method=O_DSYNC混用,后者不绕过 cache,性能更差 - 启用后,
innodb_buffer_pool_size实际占用物理内存会更“实”,若原来设到 80%,现在得压到 ≤70%,否则启动直接失败 - 验证是否生效:启动后执行
SHOW VARIABLES LIKE 'innodb_flush_method';,输出必须是O_DIRECT,不是空或async_unbuffered
swappiness=1 是减缓,memlock 才是根治
swappiness 只控制内核“倾向”,设成 0 并不能阻止 MySQL 被换出——只要系统触发 OOM killer 或内存严重不足,mysqld 依然会被踢进 SWAP。真正让 MySQL 进程免疫 SWAP 的,是 memlock。
- 在
/etc/security/limits.conf加两行:mysql soft memlock unlimited和mysql hard memlock unlimited - 在
my.cnf的[mysqld]段加:innodb_use_sys_malloc=0(配合 jemalloc 时需显式关闭系统 malloc) - 必须用 root 启动 mysqld,否则
memlock不生效(systemd 下检查User=mysql是否与 limits 用户一致) - 启动失败常见报错:
Cannot allocate memory→ 检查innodb_buffer_pool_size是否超过物理内存 70% - 开了
memlock还卡?立刻查cat /proc/mysqld_pid/status | grep -i "cap",确认CapBnd包含cap_ipc_lock
NUMA 架构下不配 innodb_numa_interleave,memlock 可能白开
在多路 CPU(尤其是 Intel Xeon + CentOS/RHEL)上,MySQL 启动时若未指定 NUMA 策略,memlock 锁住的内存可能全集中在某一个 node,而其他 node 内存充足却无法被 buffer pool 使用——结果内核还是把部分页换出到 SWAP。
5.6.27+ 版本直接加配置:innodb_numa_interleave=ON;老版本则必须在启动脚本里包一层:numactl --interleave=all mysqld_safe ...。漏掉这步,memlock 就像给车装了防爆胎却忘了打气——看着配置全对,实际一踩油门就漏。











