SSD Nodes Learn 🎉 VPS $5.50/月起
指南 Matt Connor作者: Matt Connor

VPS需要swap吗?swap大小怎么设置

大多数云镜像不自带 swap。本文说明小型 VPS 何时应创建 swap 文件、容量如何按内存和工作负载估算、vm.swappiness 的取舍,以及何时 zram 更合适。

您的 VPS 需要 swap 吗?

大多数云镜像都不包含 swap。在小型 VPS 上,通常应该添加 swap 文件。swap 不会让 1 GB 的服务器表现得像 2 GB 的服务器。它为内核提供存放冷匿名页面的位置,从而保留页面缓存的可用性,并让 OOM killer(out of memory killer,即负责选择进程并将其终止以释放内存的内核例程)成为最后手段,而不是首选手段。

简而言之:对于运行少量长期服务的服务器,小型 swap 文件值得占用相应的磁盘空间。对于某个进程经常尝试分配超过整台机器可用内存的服务器,swap 无法解决问题,还会使故障发生得更慢且更难发现。本指南的其余部分将介绍如何区分这两种情况,以及 VPS 特有的两项成本。

下面的每条命令都需要在您自己的服务器上以 root 身份运行。请直接在该服务器上执行,不要复制其他服务器的输出。

swap 实际做什么,以及它不做什么

Linux 内存分为两类。文件支持页是磁盘上已有内容的副本,包括程序以及最近读取过的所有文件。这些页面构成页缓存。匿名页没有对应的文件,包括堆和栈,以及数据库在运行时分配的大部分内存。

内存不足时,内核必须回收页面。干净的文件支持页易于回收,因为磁盘上的副本仍然存在,之后可以重新读取该页面。匿名页则不同,因为它唯一的副本在 RAM 中。没有 swap 时,内核对匿名内存只有两个选择:保留它,或终止拥有它的进程。

没有 swap 的服务器仍然会换入换出页面,只是换出的内存类型不对。内存压力增大时,内核会缩小页缓存,驱逐即将再次使用的文件页,包括正在运行的程序的可执行代码页。这些页面会以大页错误的形式重新读入。您会在 vmstatbi 列中看到磁盘读取,并在 /proc/vmstat 中看到不断增长的 pgmajfault 计数器,同时 siso 始终为 0。此时系统正在抖动,但 swap 计数器不会报告任何内容。

swap 不会增加容量。如果工作集,也就是实际正在访问的页面,总量大于 RAM,那么 swap 会把内存不足终止转换成运行极慢的服务器。有时这是您需要的取舍,因为慢速服务器仍可登录和修复,而被终止的数据库无法修复。有时情况会更糟,因为慢速服务器会持续无法通过健康检查,同时保持所有连接处于打开状态。添加 swap 前,请先决定您需要哪种结果。

云镜像为什么不自带 swap?

这是有意为之。同一个镜像必须能在云服务商提供的所有套餐上启动,因此固定的 swap 分区会浪费小型套餐的磁盘空间,对大型套餐又没有实际意义。swap 的速度还取决于虚拟机使用的底层存储,而镜像无法提前得知这些信息。镜像构建者还会优先保证行为可预测,因为在整个服务器集群中,进程立即退出比机器继续运行但每个请求都延迟数秒更容易诊断。

这些理由适用于可随时销毁的机器。需要长期保留的 VPS 则不同。您会修复它,而不是替换它,因此短暂的换页通常优于服务被终止。请将缺少 swap 视为针对他人使用场景构建的默认设置。

VPS 上应使用交换文件还是交换分区?

使用交换文件。交换分区意味着在已经分区的磁盘上调整正在运行的根文件系统大小,这会带来实际风险,但没有任何收益。使用普通命令即可创建和删除交换文件,之后也可以调整其大小,无需修改分区表。

速度不是决定因素。在 swapon 时,内核会读取一次文件的 extent map,随后直接向块设备提交 I/O,因此每次换入或换出页面时,文件系统都不会处于 I/O 路径中。在同一块磁盘上,交换文件和交换分区的性能相同。

有两个限制需要注意。不要将交换空间放在 NFS(网络文件系统)等网络文件系统上。在 btrfs 上,必须禁用该文件的写时复制并关闭压缩。因此,btrfs 自带了用于创建交换文件的辅助工具。

如何在 Ubuntu 或 Debian 上添加 swap 文件

修改前先检查现有配置。

swapon --show
free -h
findmnt -no FSTYPE /

swapon --show 输出为空表示完全没有 swap。这是全新云镜像的正常状态。findmnt 会输出根文件系统类型,而文件的创建方式取决于该类型。在大多数云镜像使用的 ext4 上,fallocate 是安全的。

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

chmod 必须在 mkswap 之前执行。跳过这一步时,mkswap 会明确提示原因:mkswap: /swapfile: insecure permissions 0644, fix with: chmod 0600 /swapfile。权限允许所有用户读取的 swap 文件,会让主机上的每个用户都能读取其他进程换出的内存内容。正常的 mkswap 随后会确认大小,输出中应有类似 Setting up swapspace version 1, size = 2 GiB (2147479552 bytes) 的行。

xfsbtrfs 上,最后一条命令可能失败并显示 swapon: /swapfile: swapon failed: Invalid argument。在 XFS 上,这是因为 fallocate 会留下未写入的 extent,因此应改为直接写入这些字节。

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

在 btrfs 上,原因是写时复制;当前版本的 btrfs-progs 会自动设置正确的标志。

sudo btrfs filesystem mkswapfile --size 2g /swapfile

无论使用哪种方式,都要执行 chmod 600、在适用时执行 mkswap,以及执行 swapon,然后确认结果。

swapon --show
free -h

swapon --show 应列出类型为 file/swapfile,大小应与请求值一致;free -h 应显示一个几乎没有已用空间的 Swap 行。全新配置中已用 swap 为 0 是正确的。内核只会在有需要时将页面移到其中。

让配置在重启后仍然生效,然后立即测试该条目。

echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
sudo swapoff /swapfile
sudo swapon -a
swapon --show

swapon -a 会读取 /etc/fstab,因此错误的配置行会立即失败,并直接显示在当前终端中。未测试过的拼写错误,可能会在非计划重启时才暴露;此时主机恢复运行,却没有启用你以为已配置的 swap。

以后要移除 swap 时,执行 sudo swapoff /swapfile,删除 fstab 中的对应行,然后执行 sudo rm /swapfileswapoff 必须先将所有已换出的页面读回 RAM,因此在繁忙的主机上可能因 swapoff: /swapfile: swapoff failed: Cannot allocate memory 失败。释放一些内存后再试。

swap 文件应设置多大?

该任务会暂存不常用的匿名页,因此需要关注的是已分配内存中真正处于空闲状态的部分,而不是套餐提供的 RAM 大小。空闲内存不会随着套餐规模同步增长,因此套餐越大,倍数越低。本指南采用以下规则。

ChartSwap file size this guide sets, by plan RAM
The data behind this chart
[
  {
    "label": "1 GB plan",
    "swap_gb": 2,
    "swap_x_ram": 2
  },
  {
    "label": "2 GB plan",
    "swap_gb": 2,
    "swap_x_ram": 1
  },
  {
    "label": "4 GB plan",
    "swap_gb": 2,
    "swap_x_ram": 0.5
  },
  {
    "label": "8 GB plan",
    "swap_gb": 4,
    "swap_x_ram": 0.5
  },
  {
    "label": "16 GB plan",
    "swap_gb": 4,
    "swap_x_ram": 0.25
  }
]

对于最小套餐,swap 为 2 GB,即 RAM 的 2 倍。这是因为 1 GB 服务器的余量很小,一次突发负载就可能触发 OOM killer。对于规格表顶部的套餐,swap 文件上限为 4 GB,即 RAM 的 0.25 倍。这是因为在共享存储上换出这么多数据需要较长时间,执行期间服务器实际上会处于不可用状态。如果磁盘配额紧张,可以适当调低这些数值,因为 swap 文件会占用实际磁盘空间。

将 swap 设置为不小于 RAM 的大小,最常见的原因是支持休眠,因为休眠会将整个内存映像写入 swap。VPS 不会进入休眠,因此该规则不适用于您。

vm.swappiness 实际会改变什么?

vm.swappiness 不是 RAM 的百分比,也不是阈值。它表示内核为回收匿名页和文件页分别分配的相对成本。默认值为 60。调低该值时,内核更倾向于丢弃页缓存。调高该值时,内核更倾向于将匿名内存换出到 swap。

这是一种双向权衡。将其设为 vm.swappiness = 10 时,数据库会让更多分配的内存保持驻留,但代价是重新读取刚从缓存中丢弃的文件。对于主要工作是提供文件服务的服务器,这个方向并不合适,因为页缓存才是该场景中发挥主要作用的部分。

将其设为 0 不会关闭 swap。该设置会让内核在内存几乎耗尽前避免回收匿名页,因此 OOM killer 会更早触发,而不是被推迟。如果不需要 swap,请删除 swap 文件。

sysctl vm.swappiness
printf 'vm.swappiness = 10\n' | sudo tee /etc/sysctl.d/99-swappiness.conf
sudo sysctl --system
sysctl vm.swappiness

单独执行 sysctl -w 的设置只会持续到下次重启,之后就会悄然失效,因此应将配置写入 /etc/sysctl.d/。5.8 及更高版本的内核接受 0 到 200 之间的值。只有在 swap 的速度接近 RAM 时,超过 100 的值才有意义,而这意味着使用 zram。

zram:以 CPU 资源换取磁盘空间的 swap

zram 是驻留在 RAM 中的压缩块设备。将其用作 swap 时,原本需要写入磁盘的页面会被压缩并保留在内存中。不产生磁盘 I/O,也不占用磁盘配额。代价是每次换入和换出页面都要消耗 CPU 时间,还要占用 RAM 保存压缩后的页面,而这些 RAM 原本可供应用程序使用。

对于匿名页面,通常公布的压缩比为 2:1 到 3:1。zramctl 会在其 DATACOMPR 列中显示实际压缩比。应进行实测,不要依据典型值规划,因为某些工作负载中的数据几乎无法压缩。

sudo apt install zram-tools

/etc/default/zramswap 中设置 ALGO=zstdPERCENT=25,然后重启服务并查看结果。

sudo systemctl restart zramswap
zramctl
swapon --show

PERCENT 表示占总 RAM 的比例,因此在 4 GB 的主机上设置为 25 时,最多会为压缩页面预留 1 GB。先从较低值开始,仅当 zramctl 显示设备已接近占满时再提高。相同文件中的 PRIORITY 设置决定内核优先填充哪个 swap:数值越高,优先级越高;使用普通 swapon 添加的磁盘 swap 文件会获得负优先级,因此内核会先使用 zram,文件只承接超出的部分。swapon --show 会在其 PRIO 列中同时显示这两项信息。在使用 systemd-zram-generator 而不是 zram-tools 的发行版中,相同设置位于 /etc/systemd/zram-generator.conf

zram 适合 CPU 余量充足但磁盘空间有限的主机。如果 CPU 配额已经不足,则不应使用 zram,因为压缩工作会与应用程序竞争同一份 CPU 资源。

VPS 上才会触发的两个 swap 陷阱

第一个陷阱是磁盘空间。创建 2 GB swap 文件后,它会立即占用套餐中 2 GB 的磁盘空间,因为必须预先分配这些空间。df -h / 会立即减少完整的文件大小,只有删除该文件后才会恢复。在小型套餐中,这占用的比例不容忽视;而根文件系统空间耗尽会导致更多问题,swap 从未解决过这些问题。该文件也会计入 du 的输出。寻找磁盘空间占用时,请记住这一点,因为 df 和 du 对磁盘空间去向的判断不一致

第二个陷阱是延迟。swap I/O 会访问主机与其他租户共享的存储,而您无法从自己的虚拟机内部看到其他租户的负载。您只能看到结果:通常很快的页面换入有时会耗时更长,等待该页面的进程会一直暂停,直到页面到达。这与 共享主机上的 CPU steal time 的原理相同,只是对象从运行队列换成了磁盘队列。请在自己的服务器上测量延迟,因为任何公开的延迟数值描述的都是其他租户及其邻居。

如何判断 swap 是否正在拖慢系统?

已使用的 swap 并不是问题。swap 流量才是问题。服务器将几百 MB 内存放在 swap 中,但没有分页活动,这通常只是把数小时内无人访问的内存移了出去。这正是预期结果。

应监控速率,而不是总量。

vmstat 1 5

siso 表示每秒换入和换出的 kibibyte 数。在运行正常的服务器上,无论 swpd 列显示什么,它们都会保持在接近 0 的水平。持续出现 so,同时 si 上升,表示页面正在写出后又被立即读回,这就是抖动。

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 2  3 1048572  38210   4096  61440  912 1180  2210  1290 1402 2890  9  7 12 72  0

该示例表明服务器运行异常。最明显的信号不在 swap 列中,而是 wa 为 72,表示 CPU 大部分时间都在等待 I/O;以及 b 为 3,表示有 3 个进程处于阻塞状态。

PSI(pressure stall information)可以更直接地回答这个问题。

cat /proc/pressure/memory
some avg10=8.42 avg60=5.11 avg300=2.03 total=1284729
full avg10=3.10 avg60=1.94 avg300=0.71 total=498210

some avg10=8.42 表示在最近 10 秒内,至少有一个任务在等待内存时发生阻塞,占这段时间的 8.42%。full 统计所有非 idle 任务都处于阻塞状态的时间,因此持续的 full 值表示已经造成可测量的性能损失,而不只是警告信号。如果该文件不存在,说明内核默认禁用了 PSI,需要在内核命令行中添加 psi=1

要查看哪些进程持有位于 swap 中的页面:

sudo awk '/^Name:/{n=$2} /^VmSwap:/ && $2+0 > 0 {printf "%10d kB  %s\n", $2, n}' /proc/[0-9]*/status | sort -rn | head

要确认 OOM killer 是否已经终止过进程:

sudo journalctl -k --grep "Out of memory"

命中记录类似 Out of memory: Killed process 2199 (mysqld) total-vm:1275860kB, anon-rss:129252kB, file-rss:0kB, shmem-rss:0kB, UID:114 pgtables:504kB oom_score_adj:0,同一秒的服务日志会显示 Main process exited, code=killed, status=9/KILL。如果服务器没有 swap,但已经看到这些日志,那么下一步最便宜的尝试就是添加 swap 文件。

交换空间并不总是正确的修复方法

交换空间只能为暂时性的内存压力或冷数据争取时间。对于不断增长直至崩溃的进程,交换空间没有帮助。它还会让问题更难监控,因为系统会额外花费几分钟进行分页,而不是快速失败并重启。

应改为限制进程。systemd 服务可在 drop-in 文件中使用 MemoryMax=MemorySwapMax=,这样无需修改应用即可使用 systemd 限制服务的内存和 CPU。容器在更高一层也提供相同的控制,设置这些限制即可阻止单个 Compose 服务耗尽整台服务器的资源。两种方式都会在日志中记录带有名称的终止操作,便于查找;否则,内核会根据评分自行选择终止对象。

应在服务器刚部署且负载较低时完成这些操作。创建交换文件并设置一个内存限制只需几分钟,这属于新 VPS 上线后的前十分钟内应完成的工作。

FAQ

添加 swap 后,1 GB VPS 会像 2 GB VPS 一样运行吗?

不会。swap 远慢于 RAM,内核只会将它判断为不活跃的页面移到 swap。swap 提供的是应对内存峰值的余量,以及存放已分配但之后从未访问过的内存的位置。如果工作负载持续读写的内存超过服务器实际拥有的内存,swap 会将内存不足终止转变为持续换页。服务器虽然不会立即退出,但响应速度会慢到无法使用。此时应增加 RAM,或限制持续增长的进程。

1 GB 或 2 GB VPS 需要多少 swap?

2 GB 足以满足这两种配置,之后无需随着 RAM 增加而继续扩容。swap 用于存放不活跃的匿名页面,而服务器上真正不活跃的内存量不会像总 RAM 那样增长。过去按 RAM 的两倍配置 swap 的规则源于休眠,因为休眠会将整个内存映像写入磁盘,而 VPS 从不休眠。超过 4 GB 后继续增加容量,通常只会让共享存储设备上的故障过程变得更长、更慢。

将 vm.swappiness 设置为 0 是停止换页的正确方法吗?

不是,而且它的实际作用并不像名称所暗示的那样。vm.swappiness = 0 不会禁用 swap。它会告诉内核,在接近内存不足之前避免回收匿名页面。这反而会提高 OOM 终止发生的可能性,而不是降低它。它还会将所有回收压力转移到页面缓存,因此文件读取更容易重新访问磁盘。如果完全不需要 swap,请运行 sudo swapoff -a 并删除 fstab 中的对应行。如果只想减少换页,请尝试 vm.swappiness = 10,并比较修改前后 vmstat 中的 siso 列。

应该使用 zram,而不是 swap 文件吗?

如果 CPU 还有余量但磁盘空间有限,请使用 zram;如果情况相反,请使用 swap 文件。zram 会压缩页面并将其保存在 RAM 中,因此完全避免磁盘 I/O,但每次换入和换出页面都会消耗 CPU,而且 zram 占用的 RAM 将无法再供应用程序使用。对于 CPU 配额较小的 VPS,这项开销会消耗你本已不足的资源。两者同时使用也很常见:在 /etc/default/zramswap 中通过 PRIORITY 为 zram 设置更高的优先级,并在其下方保留磁盘 swap 文件,用于溢出。

为什么 free 显示仍有可用内存时,OOM killer 还是运行了?

free 只报告某个时间点的状态,而内存分配会在瞬间发生。如果进程申请大块内存的速度快于内核回收内存的速度,即使平均内存使用情况看起来正常,该进程也可能被终止。使用 sudo journalctl -k --grep "Out of memory" 读取内核日志,其中会记录被终止的进程及其当时的驻留内存大小。随后确认终止是否由 cgroup 限制触发,而不是由整台服务器的内存不足触发,因为设置了 MemoryMax= 的容器或 systemd 单元会在自身达到限制时被终止,即使主机仍有可用内存。

#swap#memory#oom#zram#linux-performance