1.7G 内存的云主机该不该开 swap:8 天实测后的取舍

举报
逍遥渝枫 发表于 2026/11/03 08:30:20 2026/11/03
【摘要】 小内存机器到底该不该开 swap,两派说法各有道理。本文用一台 1.7G 内存、已连续跑 8 天的 Ubuntu 22.04 云主机做对照,拿可用内存、承诺内存、内存压力指标和换页计数器四组数据说明它究竟有没有被用上,再给出 swapfile 的创建步骤与常见报错处理、swappiness 的真实语义,以及 K8s 节点、数据库这类不该开 swap 的边界。

一台 1.7G 内存的云主机,装完 Docker 加两个容器,free -m 里 used 常年三百兆上下,buff/cache 一千多兆。这种机器最容易陷入一种纠结:开 swap 怕它把内存问题变成磁盘 IO 问题,不开又怕哪天构建镜像时被 OOM Killer 一刀砍掉。

我的结论先说:这台机器开了 2G swap,连续运行 8 天,pswpin 和 pswpout 都是 0,一次都没用上。但我仍然认为该开。 理由不是它有用,而是它在失控那一刻能把「进程被杀」换成「慢一点」。这两件事对线上服务的伤害差一个量级。

下面把这 8 天的数据和判断依据摊开,交给同样在 2G 内存机器上做取舍的人自己判断。

一、先别急着开,量清楚真实水位

很多人看 free -m 的 free 列决定要不要开 swap,这是最容易被误导的一步。Linux 会把空闲内存尽量拿去做 page cache,free 很小是正常状态,不代表内存紧张。

free -m
grep -E "MemTotal|MemAvailable|Committed_AS|SwapTotal|SwapFree" /proc/meminfo
cat /proc/pressure/memory

这台机器当时的输出:

               total        used        free      shared  buff/cache   available
Mem:            1771         292         236           2        1242        1300
Swap:           2047           0        2047

MemAvailable:    1331444 kB     # 约 1300MB 真正可用
Committed_AS:    1365456 kB     # 约 1333MB 已承诺给进程

三个数字里最重要的是后两个的对比。Committed_AS 是内核已经答应给进程的虚拟内存总量,MemAvailable 是扣掉不可回收部分后真正还能分配的。这台机器上承诺量已经略超可用量——不是因为它在崩,而是因为 Linux 默认 overcommit_memory=0,允许超额承诺,赌的是进程不会真的把承诺的内存全用满。

赌输的时候就是 OOM。而 swap 的存在,等于给这场赌局加了一层缓冲:内核可以把冷页换出去,腾出物理内存给热页,进程继续跑,只是慢。没有 swap,内核没有腾挪空间,只能挑一个进程杀掉。

PSI(Pressure Stall Information)是比 free 更诚实的指标,它直接告诉你「有多少时间因为等内存而卡住」:

some avg10=0.36 avg60=0.06 avg300=0.01 total=14125955
full avg10=0.36 avg60=0.06 avg300=0.01 total=13336793

full 的 avg300 是 0.01,说明过去 5 分钟里几乎没有出现「所有进程都因等内存而停摆」的情况。这组数据配合 MemAvailable 才是判断内存是否真的紧张的完整依据。只看 free 列就动手加 swap,往往是在解决一个不存在的问题。

二、swapfile 的创建:三条命令和三个坑

云主机一般没有独立 swap 分区(系统盘是单分区,重新分区的风险和收益不成比例),标准做法是用 swapfile。Ubuntu 22.04 上验证过的完整流程:

# 1. 创建文件。fallocate 比 dd 快得多,但某些文件系统上会留空洞,见下文
fallocate -l 2G /swapfile

# 2. 权限必须是 600,否则 swapon 直接拒绝
chmod 600 /swapfile

# 3. 格式化成 swap 并启用
mkswap /swapfile
swapon /swapfile

# 4. 验证,并写入 fstab 保证重启后自动挂载
swapon --show
echo '/swapfile none swap sw 0 0' >> /etc/fstab

swapon --show 应该输出类似:

NAME      TYPE SIZE USED PRIO
/swapfile file   2G   0B   -2

PRIO 是 -2 属于正常,表示内核自动分配优先级,不影响功能。

三个容易踩的坑,都是我在别的机器上真遇到过的:

坑一:fallocate 在部分文件系统上生成的文件带空洞。 这类文件 mkswap 可能成功,但 swapon 报 read swap header failed 或 Invalid argument。遇到就改用 dd 老老实实写:

dd if=/dev/zero of=/swapfile bs=1M count=2048 status=progress

坑二:btrfs 上根本不支持 swapfile。 如果根文件系统是 btrfs,swapon 会明确报 swapon failed: Invalid argument,此时只能另挂一个 ext4 卷,或者用 zram(压缩内存块设备)替代。本文的机器是 ext4,没有这个问题,但接手别人机器时要先 df -T / 确认。

坑三:fstab 写错会让开机卡住。 如果 swapfile 被删了而 fstab 里的记录还在,systemd 会等这个 swap 单元直到超时,表现为 SSH 连上要等一分半。稳妥的写法是加 nofail:

echo '/swapfile none swap sw,nofail 0 0' >> /etc/fstab

三、swappiness 到底该怎么设

vm.swappiness 是最容易被误传的参数。两个常见误解:

误解一:swappiness=0 等于不用 swap。 不成立。从内核 3.5 起,swappiness=0 的语义是「除非物理内存真的耗尽,否则不做匿名页换出」,而不是彻底禁止。也就是说 0 只是把触发条件推到了最晚,内核在 OOM 边缘依然会换页。这台机器就是 vm.swappiness=0,2G swap 一次没用上是正常的——它确实没到那个边缘。

误解二:调低 swappiness 能让服务变快。 在数据库这类需要稳定延迟的场景确实有帮助,因为避免热数据被换出去再换回来产生随机 IO。但对一台跑 Docker 的普通业务机,默认 60 未必差,改到 0~10 更保险一点而已。

我的做法是单独放一个配置文件,不直接 sysctl -w 写内存(重启就丢):

cat > /etc/sysctl.d/99-swap.conf <<'EOF'
vm.swappiness = 10
vm.vfs_cache_pressure = 100
EOF

sysctl --system
sysctl vm.swappiness

vfs_cache_pressure=100 是默认值,写在这里只是显式声明不调它——把它调低会让内核更倾向保留 dentry/inode 缓存,在文件操作密集的机器上能省一点内存,但代价是内存回收压力转移到匿名页上,小内存机器上不建议动。

四、怎么判断 swap 是救命还是在拖死你

开了 swap 之后真正要盯的不是「用了多少」,而是「换了多少、换得有多频繁」。free 里的 Swap used 高不高其实意义有限——一个启动后就被换出、再也没被访问的冷页,占着 swap 空间完全无害。

判断是否进入了抖动(thrashing)状态,看这三个计数器:

# 累计换入/换出的页数,单位是页(4KB)
grep -E "^(pswpin|pswpout|pgmajfault|oom_kill)" /proc/vmstat

# 实时观察,重点看 si/so 两列是否持续非零
vmstat 1 10

# 谁在占 swap
for f in /proc/*/status; do
  awk '/^Name:/{n=$2} /^VmSwap:/{if($2>0) print $2" kB\t"n}' "$f" 2>/dev/null
done | sort -rn | head -10

判读规则很直接:vmstat 的 si/so 长时间稳定在几百 KB/s 以上,同时 PSI 的 full avg10 超过 10,就是抖动,此时系统响应会明显变慢,swap 从缓冲垫变成了拖累,该做的是加内存或砍进程,而不是继续加 swap。反过来,pswpout 偶尔涨几个数、SI 一过就归零,属于正常换出,不用管。

这台机器 8 天的结果:

pswpin 0
pswpout 0
pgmajfault 7267
oom_kill 0

pswpin/pswpout 全零,说明 2G swap 完全没被碰过。pgmajfault 7267 次是文件页的缺页(主要是首次读程序和数据文件),与 swap 无关。oom_kill 为 0 是关键——8 天里没有发生任何一次内存杀进程。

换句话说:在这台机器的负载水平下,swap 是纯备用,不是必需品。 但如果哪天在这台机器上跑一次前端构建或者 docker build,编译进程吃掉 800M 到 1G 内存是常事,那一刻 1300M 的 available 就不够看了。有 swap 的结果是构建慢一点、跑完释放;没 swap 的结果是内核挑一个进程杀,可能杀的正好是你的 Docker daemon。

五、报错对照表

现象 原因 处理
swapon: /swapfile: read swap header failed fallocate 生成的文件带空洞,或没跑 mkswap 改用 dd 重写文件后重新 mkswap
swapon failed: Invalid argument 根文件系统是 btrfs,不支持 swapfile 换 ext4 分区挂载,或用 zram
swapon: /swapfile: insecure permissions 0644 权限过宽,内核拒绝 chmod 600 /swapfile
重启后 swap 没了 没写 fstab,或写错路径 检查 /etc/fstab 里的 swap 行
开机/SSH 卡一分半 fstab 里的 swapfile 已删除,systemd 等超时 加 nofail,或删掉失效记录
swapoff -a 卡住不动 待换入的页超过可用物理内存,换不回来 先释放内存(停服务/清缓存),或分批 swapoff
swappiness 改了没效果 只用了 sysctl -w,重启丢失;或被别的配置文件覆盖 写到 /etc/sysctl.d/ 并用 sysctl --system 重载
开了 swap 反而更卡 已进入换页抖动,swap 变成 IO 瓶颈 看 vmstat si/so 与 PSI,加内存或减产,不是加 swap

六、什么时候明确不该开 swap

前面说的都是「该开」的场景,反过来有三种情况要慎重:

Kubernetes 节点。 kubelet 默认要求关闭 swap,检测到 swap 开启会直接拒绝启动。可以用 --fail-swap-on=false 绕过,但调度器计算 Pod 资源时并不把 swap 算进去,节点实际能承受的负载会失真,OOM 时机变得不可预测。这类节点就该老老实实 swapoff -a,靠 requests/limits 管内存,这也是我在本地测试集群上两台机器 swap 都设 0 的原因。

跑 MySQL / Redis 这类对延迟敏感的服务。 它们工作集大、访问随机,一旦热数据被换出,查询延迟会从毫秒跳到几十毫秒,而且抖动没有规律、很难归因。这类机器的正确解法是配足内存 + 保守的 buffer 配置,不是拿 swap 兜底。

内存给得足够宽的机器。 4G 以上、负载平稳的机器,swap 常年是零使用,开着只是占 2G 磁盘。要不要开取决于你怎么看那 2G 磁盘的成本——40G 系统盘用掉 8G,多占 2G 换一个保险,我觉得划算。

七、最后

这台机器上的结论可以压缩成一句话:swap 不解决内存不足,它只是把「进程被杀」延迟成「系统变慢」,争取一段你能反应过来的时间。

所以判断顺序应该是:先看 MemAvailable 和 PSI 确认内存到底紧不紧,再决定要不要加 swap;加了之后盯 pswpin/pswpout 和 si/so 判断它是缓冲还是拖累;真要解决内存问题,加内存或减少常驻进程是唯一的正解。

以上配置与数据在 Ubuntu 22.04(内核 5.15)上验证,swapfile 相关行为在不同文件系统和内核版本上有差异,CentOS / Debian 的路径与默认值需自行确认;btrfs 与 zram 方案本文未做实测,只作方向参考。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。