mysql卡顿但cpu不高时,大概率是swap被疯狂使用;需用free -h和vmstat 1检查swapused、si/so值,确认后通过drop_caches、调低swappiness、配置memlock和o_direct根治。

MySQL卡顿但CPU不高,先看SWAP是否被疯狂使用
很多dba第一反应是查CPU或慢查询,但MySQL卡成PPT、select 1都超时,而top里mysqld的%CPU却只有10%——这时候90%概率是SWAP在拖后腿。linux内核一旦开始频繁换页(swap in/out),所有内存访问都会变成磁盘IO,MySQL这种重度内存依赖的服务会直接“窒息”。
-
free -h看SwapUsed是否持续 >1GB;更关键的是看si(swap-in)和so(swap-out)列,单位KB/s,只要这两个值 >1000 就已严重异常 -
vmstat 1中若swpd> 0 且si/so长期非零,基本可锁定SWAP问题 - 别信
used内存高就一定爆了——buff/cache高是健康表现,真正危险的是available持续低于1G且swpd上涨
临时止血:快速清空SWAP并防止立刻复用
线上不能等重启,得先让MySQL喘口气。但swapoff -a不是万能解药——它会把SWAP里所有页强行刷回物理内存,如果此时内存已不足,直接触发OOM Killer干掉mysqld进程。
- 执行前务必确认可用内存:
awk '/MemAvailable/{print $2/1024/1024 " GB"}' /proc/meminfo,确保结果 > SWAP已用量 - 安全清SWAP顺序:
swapoff -a && swapon -a(仅当内存充足时);否则改用echo 1 > /proc/sys/vm/drop_caches先释放pagecache,再尝试 - 立即压制换页冲动:
sysctl vm.swappiness=0(临时),并写入/etc/sysctl.conf固化,避免下次重启复发
根治方案:让MySQL彻底告别SWAP
只关swappiness是治标。MySQL进程本身必须被禁止换出,否则内核仍可能在内存压力下把它踢进SWAP——哪怕只换出一页,InnoDB Buffer Pool的LRU链表就全乱套。
- 在
/etc/security/limits.conf追加两行:mysql hard memlock unlimited和mysql soft memlock unlimited - 在
my.cnf的[mysqld]段加入:innodb_flush_method=O_DIRECT(绕过OS缓存,减少内存争抢) - 重启MySQL前,检查是否生效:
prlimit -p $(pgrep mysqld) | grep memlock,输出应为unlimited - 注意:开启
memlock后,innodb_buffer_pool_size不能设超过物理内存70%,否则mysqld启动会失败
容易被忽略的陷阱:大页内存与配置冲突
有人开了memlock还卡,一查发现启用了large_pages但没配hugepage,结果MySQL启动时默默退回到普通页+SWAP模式,表面配置全对,实际毫无作用。
- 检查是否真用了大页:
grep -i huge /proc/meminfo,看HugePages_Free是否 >0 - 若启用
large_pages=ON,必须提前分配hugepage:echo 2048 > /proc/sys/vm/nr_hugepages(按需调整数值) - 更稳妥的做法:先禁用
large_pages,专注把memlock+O_DIRECT+swappiness=0三件套跑通,再考虑大页优化
SWAP问题最狡猾的地方在于:它不报错,只让一切变慢。监控里看不到Error,日志里没有WARNING,连SHOW PROCESSLIST都显示“Sleep”,但你的业务请求正在排队等IO。盯住si/so和memlock状态,比优化一百条SQL都管用。