zram 压缩内存 Swap 实战指南:4GB 服务器加了"内存压缩层"之后不再卡死
【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux
凌晨两点,一台 4GB 内存的服务器 iowait 持续爬到 60% 以上,free -h里 Swap 被占用了一大截,接口响应从 80ms 涨到 3 秒。元凶是磁盘 swap:页面被换出后躺在慢速介质上,应用一旦触碰这些页面就得等磁盘 I/O。Linux 内核里的 zram 就是把一部分物理内存做成"压缩块设备"当 swap 用——数据先在内存里压缩存储,避免落盘,swap 的读写延迟能比 NVMe 再低 1~2 个数量级。这篇文章从一次真实的卡顿排查讲起,把启用、看指标、调参数一次讲透。
先看现场:swap 一启用,系统为什么就"卡"
排查这类问题,先确认三件事:swap 到底用了多少、磁盘是不是在疯狂换页、有没有进程被 OOM killer 干掉。
swapon --show # 当前启用的 swap 及优先级 free -h # 内存与 swap 总量 iostat -x 1 5 # 观察 %iowait 和磁盘 util三个信号同时出现——Swap used 持续增长、%iowait高、应用 P99 延迟飙升——基本可以判定:系统不是"内存不够",而是"换出的页面太慢"。此时加一块 zram 作为高优先级 swap,比扩容内存或加 SSD 都更直接。zram 的核心思路一句话:用 CPU 换 I/O。后面启用时你会看到它的每一个配置项都围绕这一点。
快速启用:三条命令把内存配成 zram 压缩 swap
zram 设备由内核模块创建(默认 1 个,/dev/zram0)。整套流程只有四步,注意算法必须在设置 disksize 之前选,设备初始化后算法就改不了了。
- 加载模块(若发行版的 zram-generator 已建好设备,可跳过):
modprobe zram num_devices=1 - 选算法、设容量、建 swap:
echo lz4 > /sys/block/zram0/comp_algorithm # 可选值见 cat 同一文件 echo 2G > /sys/block/zram0/disksize # 解压后数据的上限,非预占内存 mkswap /dev/zram0 swapon -p 100 /dev/zram0 # 优先级越高越先使用- 验证:
swapon --show里出现zram0且 PRIORITY 高于磁盘 swap,free -h里 Swap 总量变大,即可。
两个容易踩的坑:disksize是"可存放的解压数据上限",不是实际占用的内存,实际占用由mem_used_total决定;swapon -p的数字越大越优先,磁盘 swap 默认 0,zram 给 100 就保证了"先压内存、后落盘"。驱动实现位于 drivers/block/zram/,每个算法一个backend_*.c;更完整的官方操作序列在 Documentation/admin-guide/blockdev/zram.rst 里有逐步说明。
如何查看 zram 压缩率:读懂 mm_stat 指标
内核并不直接给你compression_ratio这类现成文件,原始数据都在/sys/block/zram0/mm_stat一行里,五个字段含义如下:
| 字段 | 含义 | 怎么用 |
|---|---|---|
orig_data_size | 已换入数据的解压后总大小 | 压缩率分子 |
compr_data_size | 压缩后实际存储的总大小 | 压缩率分母 |
mem_used_total | zram 当前实际占用内存 | 看真实开销 |
mem_limit | 允许 zram 使用的内存上限(0=不限) | 防止它吃光内存时设置 |
mem_used_max | 历史峰值占用 | 评估容量是否够 |
压缩率 =orig_data_size / compr_data_size;真正"省下来"的内存约等于orig_data_size - mem_used_total。一行 awk 就能持续出数:
awk '{printf "压缩率 %.2f | 内存节省 %.1f MB | 占用 %.1f MB\n", \ $1/$2, ($1-$3)/1048576, $3/1048576}' /sys/block/zram0/mm_stat经验值:文本型、代码型页面的压缩率通常能到 2.0 以上;长期低于 1.3 说明换入的数据可压缩性差,这时候 zram 的收益就只剩"快",没有"省"了。完整的 sysfs 属性(含compact、reset、io_stat等)在 Documentation/ABI/testing/sysfs-block-zram 里有逐条说明。看明白这行数据之后,你就能判断"卡"到底是算法选错了,还是数据本身压不动——这正好引出下一节的原理。
为什么换算法后 CPU 会飙升:zram 的压缩原理
zram 的工作路径很直白:页面被换出时,驱动分配一个 slot,把整页交给后端压缩器压缩,再把压缩结果存进内存;读回来时原路解压缩。所以每次 swap 读写都伴随一次 CPU 上的压缩/解压,这就是"用 CPU 换 I/O"的全部含义——压缩越强的算法,CPU 花得越多,换来的是更少的内存占用。
各算法的定位可以这样记:
- lz4:最快,压缩率一般约 1.8~2.0x,CPU 开销最小,默认首选;
- lz4hc:lz4 的慢速高压缩版,速度掉不少但压缩率更高;
- lzo:速度介于两者之间,属于"中庸"选项;
- zstd:压缩率最高(可调 level,还支持预训练字典参数),CPU 开销也最大。
如果你把算法从 lz4 换成 zstd 后发现 top 里 CPU 明显上涨,先别慌:只要软中断/CPU 占比在可接受范围(比如单核占用 <30%),这笔账通常还是划算的——省下的磁盘等待远比多花几个点的 CPU 值钱。真正该警惕的是:压缩率没涨多少、CPU 却涨了很多,说明数据压不动,算法选错了方向。
复盘:压缩率低、disksize 打满、想换算法分别怎么办
用一周之后,三个问题最常出现,处理方式都写在下面。
问题一:压缩率只有 1.1 左右。先确认数据构成——随机数据、已经压缩过的文件(图片、视频、日志包)基本压不动,换 zstd 也救不回来。这种情况更该做的是排查应用本身为什么吃了这么多内存,而不是硬上更高压缩率算法;反过来,数据是代码段、堆对象这类,才值得换 zstd。
问题二:swap 写满、报 disksize 不够。disksize满了只会让换出失败、退回普通 OOM 流程,不会损坏数据。扩容和换算法共用一套流程,因为设备初始化后算法和 disksize 都锁死,必须先 reset:
swapoff /dev/zram0 echo 1 > /sys/block/zram0/reset echo zstd > /sys/block/zram0/comp_algorithm echo 4G > /sys/block/zram0/disksize mkswap /dev/zram0 && swapon -p 100 /dev/zram0问题三:内存占用碎片化或想重新压缩旧数据。写1到compact触发内存整理;若启用了recomp_algorithm,写1到recompress可以让存量页面用新算法重压一遍。另外建议把mm_stat定期采进监控系统,盯两个告警:mem_used_max接近物理内存的 50%,说明该扩disksize或给mem_limit设上限;orig/compr持续低于 1.3,说明该回头看数据而不是调参数。
最后把配置口径收敛成一句:算法看场景(追速度用 lz4、追容量用 zstd),disksize设物理内存的 50%~100%,优先级高于磁盘 swap,之后定期用mm_stat复核压缩率与峰值占用。zram 不会凭空变出内存,它做的事情是让"换页"这件事从磁盘的毫秒级拉回内存的微秒级——这正是它值得出现在每台内存紧张的服务器上的原因。
【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考