MySQL卡顿但CPU低时,90%概率是SWAP拖累;需先用sysctl vm.swappiness检查值是否为60等高危值,再通过vmstat 1观察si/so确认换页活动,最后调低swappiness至1–10、配置memlock和O_DIRECT根治。

MySQL卡顿但CPU很低,90%概率是Swap在拖后腿——别急着调my.cnf,先看vm.swappiness有没有被设成60这种默认高值。
怎么看vm.swappiness当前值是否危险
直接执行:sysctl vm.swappiness。输出如果是60或更高,就已踩中数据库服务器的典型配置雷区。桌面系统可以容忍,但MySQL这类内存密集型服务,vm.swappiness=60意味着内核只要发现内存使用率一过40%,就开始主动把匿名页(比如InnoDB Buffer Pool里的页)往Swap里扔——而磁盘IO速度比内存慢3–4个数量级。
更关键的是:这个值不反映Swap是否正在被用,只反映“倾向”。所以即使free -h显示SwapUsed为0,只要vm.swappiness是60,它随时可能开闸放水。
- 安全阈值:生产MySQL建议设为
1~10,1最激进,10更稳妥 - 别信“设成0就彻底禁Swap”——
vm.swappiness=0只是让内核“仅在OOM前最后一刻才考虑Swap”,设备本身仍挂着,swpd非零、si/so飙高照样发生 - 云主机尤其要注意:Ubuntu 20.04+ 默认带
/swap.img,光改sysctl没用,得确认swapon --show输出为空
临时调低vm.swappiness要立刻生效
线上不能等重启,用sysctl直接压到最低试探效果:
sudo sysctl vm.swappiness=1
执行后立刻用vmstat 1观察si和so列:如果之前持续>1000 KB/s,现在掉到0或个位数,基本锁定Swap是元凶。
- 别用
echo 1 > /proc/sys/vm/swappiness——虽然也能用,但sysctl会校验取值范围,避免输错导致内核静默失败 - 如果调完没变化,检查是否被容器限制:Docker启动时没加
--memory-swappiness=1,宿主机设置对容器内MySQL无效 - 调完记得
watch -n1 'cat /proc/sys/vm/swappiness'盯几秒,防止某些运维脚本自动回滚
永久生效必须写进/etc/sysctl.conf并重载
编辑配置文件:sudo nano /etc/sysctl.conf,在末尾追加一行:
vm.swappiness=1
然后执行:sudo sysctl -p。注意:这步不是可选,漏了就等于白改——很多团队改完文件忘了-p,重启后才发现还是60。
- 不要往
/etc/sysctl.d/下乱建conf文件:部分发行版加载顺序不保证,sysctl -p可能不读 - 改完验证:
sysctl vm.swappiness必须输出1,且cat /etc/sysctl.conf | grep swappiness能捞出那行 - Ansible或Salt批量部署时,务必加
sysctl reload任务,不能只copy文件
vm.swappiness=1之后还卡?说明Swap进程没真正锁住
参数调低只是降低倾向,不代表MySQL进程不会被换出。真正根治要三件事齐上:
- 在
/etc/security/limits.conf加:mysql soft memlock unlimited和mysql hard memlock unlimited - 在
my.cnf的[mysqld]段加:innodb_flush_method=O_DIRECT - 重启MySQL后,立刻执行:
prlimit -p $(pgrep mysqld) | grep memlock,输出必须是unlimited,否则memlock没生效
最容易被忽略的一点:开了memlock但innodb_buffer_pool_size设得太大(比如占物理内存85%),MySQL启动会静默失败——查journalctl -u mysql才能看到Cannot allocate memory报错,而不是日志里写“启动成功”。



















