Linux 内存与 swap 调优实战:swappiness、zram 与 OOM 防线

关于 swap 的说法流传着不少互相矛盾的版本:有人把 swappiness 设成 0 当作优化,有人看到 swap 用了几个 G 就慌着要扩容内存,还有人干脆关掉 swap 然后在内存峰值时被 OOM killer 直接杀掉核心进程。本文按「机制 → 参数 → 扩容 → 压缩内存 → 压力观测」的顺序,把内存与 swap 调优一次理清。

适用环境:Rocky Linux 9 / RHEL 9(内核 5.14),Ubuntu 22.04 参数语义一致,zram 的启用差异会单独注明。

先看懂内存回收机制

Linux 内存大头分两类:页缓存(page cache),即文件数据的缓存,可回收、丢弃成本低;匿名页(anonymous pages),即进程堆、栈等没有文件后备的内存,回收只能写进 swap。内存紧张时内核按 LRU 链表回收,先动页缓存还是先动匿名页,比例就由 swappiness 等参数调节。理解调优之前先分清两个常见误判:

  • free 命令里 free 列很小不代表内存不足,要看已计入可回收缓存的 available 列;
  • swap 的 used 不为零不代表正在内存抖动,冷数据被换出后长期躺在 swap 里是正常现象,要看的是持续的换入换出速率(vmstat 的 si/so 列)。

前置条件

  • 具有 sudo 权限的账号;
  • 知道本机内存总量、当前 swap 构成(分区、文件还是 zram):swapon --show 与 cat /proc/swaps 可查;
  • 调优前先记录一段业务高峰期的基线数据(vmstat、sar 或云监控),否则无从对比效果。

vm.swappiness 的真实语义

vm.swappiness 取值 0–200,默认 60。它不是「内存用到百分之多少才开始用 swap」的开关,而是回收时在「回收页缓存」与「回收匿名页」两条 LRU 链之间的权重偏向:值越大越倾向于换出匿名页,越小越倾向于丢页缓存。常见误设是把它改成 0 并宣称「禁用 swap」——0 并不禁用 swap(内核 3.5 之后 0 的含义是极力避免换出匿名页,直到内存压力非常大才动),真正的效果是把回收压力全压到页缓存上,数据库类负载的文件缓存被反复冲刷,抖动反而更严重。

经验取向:通用服务器保持默认 60 或微调到 30–40;以数据库为主、希望尽量保住匿名页不被换出的机器常设 1–10,注意是低而不是 0;大量顺序读写、缓存命中收益低的文件服务器可适当调高。修改方式与持久化:

sysctl vm.swappiness
echo 'vm.swappiness = 10' | sudo tee /etc/sysctl.d/99-memory.conf
sudo sysctl --system
cat /proc/sys/vm/swappiness

vm.overcommit 的口径

vm.overcommit_memory 决定内核对内存申请的态度:0(默认)是启发式,明显过量的申请拒绝、大额申请放行;1 是永远放行,适合确知自己在做什么的特殊场景;2 是严格模式,按 overcommit_ratio 的上限卡总量,申请超限直接失败。很多中间件(如 Redis)的官方建议是设为 1 以保证 fork 时不因启发式判断被拒。改它之前要清楚:它不影响真实内存回收,只影响「申请阶段给不给」,设错方向的表现是应用莫名报 Cannot allocate memory,或在真压力下把 OOM 推迟到更不可控的时刻。

swap 扩容的标准流程

扩容优先用 swap 文件,灵活、可回退;分区扩容要动磁盘布局,除非新机器初始化,不建议事后折腾。新建一个 8G 的 swap 文件:

sudo fallocate -l 8G /swapfile2
sudo chmod 600 /swapfile2
sudo mkswap /swapfile2
sudo swapon /swapfile2
echo '/swapfile2 none swap sw 0 0' | sudo tee -a /etc/fstab
swapon --show
free -h

几个细节:权限必须 600,mkswap 会有明确警告别忽略;用 fallocate 在 XFS 上个别老版本曾有空洞问题,新内核已修复,实在不放心可用 dd if=/dev/zero bs=1M count=8192 慢工出细活;在 copy-on-write 文件系统(如 Btrfs)上 swap 文件需要额外处理(关 CoW),Rocky 默认 XFS 无此顾虑。容量口径上别再套「内存两倍」的老经验:现代机器的 swap 定位是峰值缓冲与冷页收容,内存 64G 的机器配 8–16G 已经足够,真正决定体感的是换入换出速率,不是总量。

zswap 与 zram:两种压缩路线

当 swap 设备是慢速磁盘、或根本不想落盘时,用 CPU 换空间的两条路线值得考虑:

  • zswap:内核内置的压缩缓存层,匿名页先被压缩放进内存池,池满才写真正的 swap 后端。需要后端 swap 存在,适合「磁盘 swap 太慢」的机器。通过内核参数启用:
sudo grubby --update-kernel=ALL --args="zswap.enabled=1 zswap.compressor=lz4 zswap.max_pool_percent=20"
cat /sys/kernel/debug/zswap/pool_total_size
  • zram:把一块压缩内存做成块设备直接当 swap,完全不落盘,适合小内存机器与云主机。Rocky Linux 9 自带 zram-generator,装配后改配置即可:
sudo dnf install -y zram-generator
cat /etc/systemd/zram-generator.conf
sudo systemctl restart systemd-zram-setup@zram0.service
zramctl
[zram0]
zram-size = ram / 2
compression-algorithm = lz4

Ubuntu 22.04 上对应包是 zram-tools,改 /etc/default/zramswap 里的 PERCENTAGE 后重启 zramswap 服务。两条路线的共同代价是 CPU 开销与压缩比不及预期的风险;内存大户通常只用其一,且压缩池不超过物理内存的 20–50%。

用 PSI 在 OOM 前看清压力

比起事后翻 OOM 日志,更重要的是提前感知压力。内核的 PSI(Pressure Stall Information)位于 /proc/pressure/memory,记录任务因等内存而停顿的时间比例:

cat /proc/pressure/memory
vmstat 1 5
sar -r 1 5

PSI 输出里 some 表示至少有任务在等内存,full 表示所有非 idle 任务都在等。avg10/avg60 是最近 10 秒、60 秒的停顿占比:业务机器上 some avg10 长期大于 1–2,说明压力已实质影响吞吐,该扩容内存或给关键服务设 MemoryMin(systemd 的内存保障),而不是继续拧 swappiness。OOM 真发生后,用 journalctl -k -b 0 | grep -i "out of memory" 查 killer 的决策记录。

调优前后的验证方法

  • 固定一条业务高峰期的对照基线:vmstat 的 si/so、PSI 的 some avg60、应用自身的 P99 延迟,三项一起看;
  • 一次只改一个参数,观察至少一个完整业务周期(包含夜间批处理这类隐藏峰值);
  • 改 swappiness 后用 grep -E 'pswpin|pswpout' /proc/vmstat 对比换入换出页数增量,而非只看 used 总量;
  • 启用 zram/zswap 后确认 zramctl 或 zswap 统计里压缩池在增长且压缩比大于 1,否则只是在白占内存。

常见问题

swap 用满了是不是要出事。看速率不看总量:si/so 持续为 0、业务延迟正常,就是冷页在 swap 里安静躺着,不用处理;si/so 持续非零且 PSI 升高,才是真压力,该扩内存或查内存泄漏。

关掉 swap 行不行。关掉后内存压力只能靠丢页缓存与 OOM 解决,峰值时进程被直接杀死,没有缓冲带。Kubernetes 旧版本曾要求关 swap 是出于调度可预期性的取舍,普通服务器不建议跟风;真要关,也先把 MemoryMin、cgroup 限额这些防线配好。

改了 swappiness 立刻生效却看不到变化。该参数只影响之后回收时的偏向,不会主动把已在 swap 里的页面搬回来。想立即清空 swap 用 swapoff -a && swapon -a,前提是可用内存足够装下换出的量,否则 swapoff 会长时间卡住。

zram 开了以后内存反而更紧。多半是 zram-size 设得过大,压缩池把物理内存吃掉太多。按物理内存的 25% 起步,再依据实际压缩比调整。

小结

内存调优的顺序应该是:先用 PSI 与 vmstat 建立压力观测,再小步调整 swappiness 这类偏向参数,容量不足时用 swap 文件平稳扩容,磁盘太慢时再上 zram/zswap 用 CPU 换延迟。把「看速率不看存量」记牢,swap 就不再是玄学,而是一条可观测、可回退的 OOM 防线。

发表回复

后才能评论