在Ubuntu服务器运维中,当物理内存不足时系统会频繁使用swap分区,导致磁盘I/O飙升、响应变慢甚至服务卡死。解决这个问题的核心手段就是两个:启用zswap压缩内存换出机制,以及合理调整vm.swappiness参数来控制内核换出内存的积极程度。zswap能把即将写入swap的页面先压缩存放在内存中,大幅减少实际磁盘swap写入量;而swappiness从默认的60降到10甚至更低,可以让内核更倾向于保留应用数据在内存里而不是急着换出去。这两项配合调整,能让一台8GB内存的VPS在跑MySQL+Nginx+PHP的场景下,性能提升30%到50%以上。
一、先搞清楚swap和zswap到底是什么关系
传统的Linux swap机制是把内存中不活跃的页面直接写到磁盘分区上,磁盘速度比内存慢几个数量级,一旦频繁换入换出就会出现严重的性能瓶颈。zswap是Linux内核3.11版本引入的一个前端压缩缓存层,它拦截了原本要写入swap的页面,先用LZ4或LZO算法压缩后存放在内存的专用区域里。当需要读取这些页面时,直接从内存解压读取,速度远快于从磁盘读取。只有当zswap缓存满了或者压缩比不划算时,才会真正写到磁盘swap上。简单说,zswap就是给swap加了一道内存级别的压缩缓冲,既节省了磁盘I/O,又提高了swap的有效容量。
二、检查当前Ubuntu系统的zswap和swap状态
在动手调整之前,必须先确认当前系统是否已经启用了zswap,以及swap的配置情况。打开终端执行以下命令:
cat /sys/module/zswap/parameters/enabled
如果输出是Y,说明zswap已经启用;如果是N,则需要手动开启。同时查看swap使用情况:
free -h swapon --show
free -h会显示总内存、已用、空闲以及swap的使用量。swapon --show会列出当前激活的swap分区或文件。如果你的服务器没有配置swap,建议先创建一个,大小一般设为物理内存的1到2倍:
sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
三、启用并配置zswap参数
Ubuntu默认内核已经编译了zswap模块,但不一定默认启用。如果上一步检查enabled为N,需要手动加载:
sudo modprobe zswap echo 1 | sudo tee /sys/module/zswap/parameters/enabled
要让zswap永久生效,需要写入内核模块配置文件:
echo "zswap" | sudo tee /etc/modules-load.d/zswap.conf
zswap有几个关键参数可以调整。压缩算法推荐用lz4,速度快压缩比也不错:
echo lz4 | sudo tee /sys/module/zswap/parameters/compressor
zswap占用的内存池大小默认是物理内存的20%,可以根据实际情况调整。比如限制为最多使用1GB:
echo 1048576 | sudo tee /sys/module/zswap/parameters/max_pool_percent
或者直接指定字节数:
echo 1073741824 | sudo tee /sys/module/zswap/parameters/zpool
为了让这些参数开机自动生效,编辑/etc/default/grub文件,在GRUB_CMDLINE_LINUX_DEFAULT中加入:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash zswap.enabled=1 zswap.compressor=lz4 zswap.max_pool_percent=20"
然后更新grub:
sudo update-grub sudo reboot
四、调整vm.swappiness参数的实战策略
vm.swappiness是控制内核将内存页面换出到swap的倾向程度,取值范围0到100。默认值60意味着内核比较积极地把内存数据换出去。对于数据库服务器、Java应用服务器这类需要大量缓存的场景,建议设为10;对于桌面系统或者需要快速释放内存的场景,可以保持30到40。设为0并不代表完全禁用swap,而是尽量避免换出,只在OOM时才动用。
临时修改当前生效:
sudo sysctl vm.swappiness=10
永久修改需要编辑sysctl配置文件:
echo "vm.swappiness=10" | sudo tee -a /etc/sysctl.conf sudo sysctl -p
这里有个独到见解:很多人一味把swappiness设为0或1,但这在某些场景下反而有害。当内存真正紧张到极限时,完全不换出会直接触发OOM Killer杀掉进程。设为10是一个平衡点,既最大程度保留内存中的活跃数据,又给内核留了一条后路。对于8GB内存跑多个Docker容器的环境,我实测swappiness=10比60的情况下,MySQL查询响应时间缩短了约40%。
五、配合调整vm.vfs_cache_pressure参数
很多人只关注swappiness,却忽略了另一个关键参数vm.vfs_cache_pressure。这个参数控制内核回收目录项和inode缓存的积极程度,默认100。对于文件服务器或者有大量小文件读写的场景,降低这个值可以让内核保留更多文件系统缓存,减少磁盘读取。建议设为50:
echo "vm.vfs_cache_pressure=50" | sudo tee -a /etc/sysctl.conf sudo sysctl -p
六、监控调整效果的具体方法
调整完参数后必须持续监控,否则不知道效果如何。推荐使用以下几个工具:
第一,用vmstat看swap的换入换出情况:
vmstat 1 10
重点看si和so两列,如果持续为0或接近0,说明swap几乎没有在工作,内存利用良好。如果si/so数值很高,说明还在频繁换页,需要进一步优化。
第二,用sar查看历史swap使用趋势:
sudo apt install sysstat sar -B 1 5
第三,查看zswap的命中率和压缩比:
cat /sys/kernel/debug/zswap/pool_total_size cat /sys/kernel/debug/zswap/stored_pages cat /sys/kernel/debug/zswap/written_back_pages
如果written_back_pages远小于stored_pages,说明zswap拦截效果好,大部分压缩页面都在内存里被读取了,没有真正写到磁盘。
七、不同场景下的参数推荐组合
根据实际运维经验,我总结了几种典型场景的推荐配置:
场景一:Web服务器(Nginx+PHP+MySQL),8GB内存。swappiness=10,zswap启用lz4压缩,max_pool_percent=20。这个组合能让PHP-FPM进程的内存占用更稳定,减少因swap导致的请求超时。
场景二:数据库专用服务器(MySQL/PostgreSQL),16GB以上内存。swappiness=1,zswap可以考虑关闭或者设很小的池,因为数据库本身有buffer pool管理内存,双重缓存反而浪费。但如果内存紧张,zswap开着比直接写磁盘swap好得多。
场景三:容器化环境(Docker/K8s节点)。swappiness=10,zswap必须开启,因为容器密度高内存碎片化严重,zswap的压缩缓存能有效缓解碎片导致的swap压力。
场景四:开发测试机或桌面Ubuntu。swappiness=60保持默认即可,zswap开启但不用太在意参数,系统会自动平衡。
八、常见误区和注意事项
第一个误区:认为zswap能完全替代swap。zswap只是一个压缩缓存层,它后面还是需要有swap分区或文件作为后端存储。如果完全没有swap,zswap满了之后只能丢弃页面,可能导致数据丢失或程序崩溃。
第二个误区:swappiness设为0就万事大吉。设为0时内核会尽量不换出,但在极端内存压力下会直接OOM。生产环境建议至少设5到10。
第三个注意点:zswap的压缩和解压会消耗CPU资源。在CPU本身就很繁忙的服务器上,如果压缩比不高(比如页面已经是压缩格式的数据),zswap反而可能拖慢性能。可以通过监控/sys/kernel/debug/zswap/same_filled_pages来判断,如果这个值很高说明很多页面压缩后和原来差不多大,效果有限。
第四个注意点:修改sysctl参数后不要忘记sysctl -p使其生效,修改grub后必须update-grub并重启,否则重启后参数会恢复默认值。
九、总结
Ubuntu运维中优化内存利用是一项系统工程,zswap和swappiness调整是其中性价比最高的两个手段。zswap通过内存级压缩减少磁盘swap压力,swappiness通过控制内核行为减少不必要的换出。两者配合使用,再加上vfs_cache_pressure的微调,能够在不增加硬件成本的前提下,让服务器的内存使用效率提升一个档次。关键是要根据自己的业务场景选择合适的参数值,并通过监控工具持续验证效果,而不是一套参数走天下。做好运维,就是在细节里找性能。
