Linux 的 swappiness 默认值太高把数据库进程换出内存拖垮了响应
问题现象
有台跑 MySQL 的虚拟机,内存 16G,平时 QPS 不算高,但每隔一两天就会出现一波响应尖刺:原本 5 毫秒能返回的查询,突然要等几百毫秒甚至秒级,持续一两分钟后又莫名其妙恢复。业务方报"数据库卡顿",但 CPU 没满、磁盘 IO 也没看到明显瓶颈,监控上唯一异常的是 si/so(swap in/out)那一栏在那个时段突然飙高。
排查过程
我先排除了慢查询:把那段时间的所有 SQL 抓出来,没有新增的大查询,索引也都正常。接着看 top,发现 MySQL 进程占用的 RES 一直稳定在 6G 左右,但 VIRT 有 9G 多,说明有一部分被换到了 swap。再点开 vmstat 1 观察:
procs -----------memory---------- ---swap-- -----io----
r b swpd free buff cache si so bi bo
2 0 2100000 800000 ... 120 0 ...
si 那一列(从 swap 读回内存)在卡顿时段稳定有几十到上百。基本确认:MySQL 的部分内存被换到 swap 了,查询要用到时再换回来,于是卡。
根因
问题出在 vm.swappiness。这个参数告诉内核"有多积极地把匿名内存页换到 swap",默认值是 60,相当激进。在内存看起来还算宽裕(free 还有几百 M)时,内核仍会按 60 的倾向把"看起来不怎么活跃"的进程内存换出去。MySQL 的 buffer pool 大部分时间处于相对空闲状态(没有持续被访问),就被内核判定为可换出对象,默默挪到了 swap。等到查询真正命中那些页,又得从 swap 读回来,延迟就上去了。
解决办法
对跑数据库的机器,把 swappiness 调低,让内核尽量别碰匿名页:
# 临时生效
sysctl vm.swappiness=10
# 永久生效
echo 'vm.swappiness=10' >> /etc/sysctl.conf
sysctl -p
10 是比较稳妥的值,保留了极少 swap 作为安全垫;如果内存非常充裕、完全不想换,可以设 1(不要设 0,0 在旧内核下某些内存回收路径会出问题)。改完之后观察几天,si/so 基本归零,卡顿尖刺消失。
顺带把已经换出去的页主动收回来:
# 先确保有足够空闲内存,再清空 swap
swapoff -a && swapon -a
注意这步有风险——如果空闲内存不够 swap 里的数据,swapoff 会卡住甚至 OOM,执行前先 free -m 确认 available 大于 swpd。
小结
swappiness 默认值 60 是为桌面/通用场景设计的,对数据库这类"内存就是命"的服务并不合适。经验值:数据库机器设 1~10,应用服务器设 10~30,通用 60 即可。排查"偶发卡顿"时,别只盯着 CPU 和磁盘,把 vmstat 里的 si/so 加上,往往能少走很多弯路。
- 点赞
- 收藏
- 关注作者
评论(0)