一、swap 在现代服务器上还有没有用
这几年有不少文章说「服务器内存够大就不需要 swap,加了反而拖慢系统」,这句话只在一种情况下成立:内存确实充足,而且你完全不在乎偶发的内存峰值把进程打死。对个人站长的小内存 VPS(1G、2G 居多)来说,关掉 swap 往往不是优化,而是把「变慢」换成了「被 OOM Killer 杀掉」。
swap 的真实作用是给内存压力提供一个缓冲层。它不提高性能,但它让系统在瞬时高峰时有地方可退:一次备份任务、一次图片批量处理、一个没限制好内存的 PHP 进程,都可能把可用内存瞬间吃光。有 swap 的话,冷页面被换出去,服务继续跑,只是慢一点;没 swap 的话,内核直接挑一个进程杀掉,通常挑中的是占内存最多的那个——也就是你的 MySQL 或 PHP-FPM。
二、先搞清楚三种内存数字
判断要不要加 swap、该加多少,必须看懂 free -m 里那几列,很多人只看第一行的 free,那是错的:
free -m- total:物理内存总量。
- used:已用,但这个数字包含 page cache。
- buff/cache:内核用来缓存文件数据的内存。这部分可以随时回收,不算真正的压力。
- available:真正可用的估算值,内核会考虑可回收的缓存。看这个,不看 free。
一个典型的误判:free 只剩 80MB,看着要爆了,但 available 还有 600MB,这说明内存很健康,buff/cache 在正常工作。反之,如果 available 长期低于总量 10%,同时 si/so(swap in/out)持续不为零,那才是真的内存不足。
swap 的使用情况用 vmstat 看更准确:
vmstat 1 10
# 关注 si / so 两列,单位 KB/s
# si 持续大于 0 = 正在从 swap 换入,说明内存压力大
# so 持续大于 0 = 正在往 swap 换出,长期这样说明 swap 太小或内存不够三、swap 该给多大
网上流传的「内存的两倍」是二十年前的经验,那时候内存才 256MB。现在更合理的经验值是:
- 1G 内存:2G swap。小站遇到编译、备份、Composer 安装时很吃内存。
- 2G 内存:2G~4G swap。
- 4G 及以上:2G swap 就够,主要作用是兜底而不是日常使用。
- 有 hibernation 需求的桌面系统才需要严格等于内存大小,服务器不需要。
更重要的是两个内核参数:
# 1 = 只在内存真正不足时才用 swap(推荐服务器)
# 0 = 允许内核提前换出,可能带来不必要的磁盘 IO
cat /proc/sys/vm/swappiness
# 临时调整
sysctl -w vm.swappiness=10
# 永久生效
echo "vm.swappiness = 10" >> /etc/sysctl.d/99-swap.conf
sysctl --system另一个参数 vm.vfs_cache_possible 其实叫 vm.vfs_cache_pressure,默认 100。如果你的站点是小文件很多(比如大量静态页),把这个值调低到 50 可以让内核更愿意保留 dentry/inode 缓存,减少反复读目录的开销。但这不是万能药,调太低会导致内存被缓存占满,遇到突发流量时来不及回收。
四、加 swap 的完整步骤(文件方式)
云服务器上加 swap 有两种方式:分区和文件。分区性能略好但操作麻烦、不能动态调整;文件方式灵活、可随时扩缩,对小站完全够用。以下是标准流程:
# 1) 创建 2G 的 swap 文件(bs 用 1M,count 2048)
fallocate -l 2G /swapfile
# 如果没有 fallocate 或者文件系统不支持,用 dd
# dd if=/dev/zero of=/swapfile bs=1M count=2048 status=progress
# 2) 权限必须 600,否则 swapon 会报 "insecure permissions"
chmod 600 /swapfile
# 3) 格式化为 swap
mkswap /swapfile
# 4) 启用
swapon /swapfile
# 5) 确认
swapon --show
free -m然后写入 /etc/fstab 让开机自动挂载:
echo '/swapfile none swap sw 0 0' >> /etc/fstab这里有几个高频踩坑点。第一,fallocate 在 XFS 上生成的 swap 文件可能带 holes,swapon 会报 swapon failed: Invalid argument,改用 dd 就好。第二,忘记 chmod 600 是最常见的失败原因,报错信息里明确写了 insecure permissions。第三,/etc/fstab 写错会导致系统启动失败进 emergency mode,改完务必先 mount -a 验证,再重启。
五、swap 打开了但没用上?检查这几点
有些人加了 swap 之后发现 Swap: 0 0 0,即总量有了但从不使用。这通常是正常的——swappiness=10 的情况下,内核会尽量不用 swap,只有真正压力大时才换页。swap 使用量为 0 恰恰说明内存充足,不是故障。
但如果是下面几种情况,就要排查了:
swapon --show为空 → 没真正启用,检查 fstab 和systemctl daemon-reload。- swap 用满但
si/so很高 → swap 太小,需要扩容;扩容不能直接覆盖文件,要先swapoff。 - 内存明明有空余却大量使用 swap → 检查 swappiness 是不是被设成了 100,或者有容器使用了 cgroup 限制导致内核行为异常。
- Docker 容器内看不到 swap → cgroup v2 下容器的 swap 需要单独配置
--memory-swap,宿主机有 swap 不代表容器能用。
六、swap 的代价:什么时候它会拖慢你
swap 放在机械盘上是灾难性的——随机 IO 只有几百 IOPS,换页延迟能达到几十毫秒,服务响应直接从 50ms 变成 5s。所以有一条硬性建议:swap 必须放在 SSD/NVMe 上。云服务器基本都是 SSD,问题不大;如果是自建机器用了大容量 HDD 做数据盘,千万别把 swap 放在那里。
更严重的情况是「swap 抖动」(thrashing):内存严重不足,系统不断换入换出,CPU 全花在页表操作上,磁盘 IO 100%,服务完全卡死。这种状态下系统看起来还活着(SSH 能连但要等很久),实际上已经在做无用功。判断方法是看 vmstat 的 si/so 持续几百 KB 以上、同时 wa 很高。这时候正确的做法是减少内存占用或加内存,而不是加更多 swap。
七、swap 和 OOM Killer 的关系
很多人以为有 swap 就不会被 OOM 杀掉,这是误解。OOM Killer 在「物理内存 + swap 全部耗尽」时仍会启动。swap 只是把触发时间往后推。真正决定谁被杀的,是内核给每个进程算的 oom_score:
# 查看各进程的 oom 分数(分数越高越容易被杀)
for p in /proc/[0-9]*; do
[ -r "$p/oom_score" ] && printf "%s %s\n" "$(cat $p/oom_score)" "$(cat $p/comm 2>/dev/null)"
done | sort -rn | head -10如果确认某个关键进程(比如 sshd、你的主数据库)不该被杀,可以用 oom_score_adj 降低它的分数:
# -1000 表示完全免疫(慎用,可能导致系统卡死而不是优雅重启)
echo -500 > /proc/$(pidof mysqld)/oom_score_adj更稳妥的做法是给 systemd 服务加配置:
# /etc/systemd/system/mysqld.service.d/oom.conf
[Service]
OOMScoreAdjust=-500然后 systemctl daemon-reload,重启服务。注意不要给 PHP-FPM 的每个 worker 都设保护,worker 才是该被杀的——优先保护数据库和 SSH,牺牲可重启的业务进程,这才是合理的取舍。
八、小站的内存规划顺序
把顺序理清楚,比盲目加 swap 有效得多:
- 先看 available 和 si/so,确认是不是真的内存不足,而不是被 buff/cache 迷惑。
- 给每个服务设上限:MySQL 的
innodb_buffer_pool_size、PHP-FPM 的pm.max_children、Redis 的maxmemory。三个都不限制,内存必然打架。 - 加 swap 兜底,swappiness 设 10,只作为缓冲。
- 加监控:内存使用率、swap 使用量、si/so、OOM 日志四条一起看。
- 最后才考虑升级配置。升级是最贵的方案,前面的步骤没做完就升级,通常只是把故障推迟几周。
个人站长常见的误区是把 swap 当成「免费内存」,加了 4G swap 就觉得 2G 内存能跑 6G 的负载。技术上能跑,但性能会掉到不可用的程度,而且掩盖了真正的问题(比如某个缓存配置写错导致内存泄漏)。swap 是安全气囊,不是发动机。
九、小结
swap 的正确用法是:给足大小(1G 内存配 2G swap)、放在 SSD 上、swappiness 调到 10、纳入监控,并且清楚它只提供缓冲不提供性能。判断内存状态看 available 和 si/so,不要看 free。真正治本的办法是给每个服务设内存上限,这比任何内核参数都管用。