Swap 这玩意儿,玩 Linux 的人迟早会撞上。装个 Ubuntu 默认分区就有 swap,很多人一路 Next 过去也没管过;但当你真碰到一次内存告急、进程被 OOM Killer 干掉之后,才会回过头来认真研究它。我早期踩过不少坑,比如 swap 设在磁盘上导致系统卡成 PPT、笔记本休眠直接失败,这些都是 swap 配置不当引起的。这篇东西我打算从头到尾讲清楚:什么时候需要 swap、怎么用 swap 文件而不是 swap 分区、创建之后怎么验证、内核参数怎么调、出现问题怎么查。不管你是刚入门的 Linux 新手,还是已经在生产环境折腾过一轮的运维,都会找到能直接拿去用的东西。
先说结论:如果你还在用 swap 分区,并且正考虑要不要迁移到 swap 文件,我的建议是——认真看完下面这段对比,这可能是你今年做过最值的十分钟。
1. 为什么要认真对待 Swap
1.1 Swap 到底解决了什么问题
很多人有个误区,觉得 swap 就是“内存不够时拿来凑数的慢速内存”。这个说法不算错,但不完整。Swap 的真实定位是“内存的溢出保护 + 低活跃数据的搬运工”,它的价值体现在三个层面:
第一,防止物理内存耗尽时系统崩溃。Linux 内核有一个 OOM Killer 机制,当内存被完全吃光时,内核会主动挑进程杀掉来保证系统存活。如果你没有 swap,触发 OOM 的概率会大幅提升,而且杀掉的往往是内存占用最大的进程——可能就是你的数据库或者 Java 应用。有了 swap 做缓冲,OOM Killer 的触发会被推迟,给运维留出反应时间。
第二,承接冷数据,释放物理内存给热数据。内核的页面回收机制会把很久没访问的内存页换到 swap 里,腾出的物理内存拿去做 page cache(磁盘缓存)。这在处理文件读写密集型任务时效果非常明显。打个比方:你的物理内存像是办公桌上的空间,经常要用的文件放在手边(page cache),三个月没翻过的旧档案扔到旁边的大文件柜里(swap),桌面自然就干净了。
第三,支撑休眠与内存碎片的应对。笔记本用户尤其依赖这点。Hibernate 会把内存镜像写到 swap 区域,如果你用的是 swap 文件,内核通过resume=参数指定文件路径(需要 initramfs 支持),如果没有正确的 swap 配置,休眠之后你是醒不来的。
但注意,swap 不是万能药。物理内存本身严重不足时,swap 只是让系统“勉强活着”,而不代表“能用”,因为磁盘 IO 再怎么快也比内存慢几个数量级。真正合理的方案是:内存规划为主,swap 兜底为辅。
1.2 Swap 文件 vs Swap 分区:为什么我推荐用文件
传统做法是装系统时给 swap 单独分一个分区,但我在生产环境和个人机器上实践下来,swap 文件的灵活度完胜分区,原因很实在:
- 不需要动分区表。硬盘分区已经定好之后,你想增大 swap,传统方案得用 GParted 之类的工具重新调整分区,扩容期间搞不好还要卸载分区或者启动 Live CD,风险不小。Swap 文件就简单了,改大小重新建一个文件即可,老文件
swapoff之后删除,完全不碰原有分区结构。 - 便于精细化控制。你可以针对不同工作负载创建多个 swap 文件,给不同优先级(
pri参数),比如有一个快速的 NVMe 上的小 swap 文件给高频冷数据周转,再配一个大容量的 SATA 上的 swap 文件做兜底。分区方案想这么玩就麻烦很多。 - 云服务器场景适配性极强。很多 VPS 或者云主机用的虚拟磁盘本身不支持分区操作(尤其 LVM 或某些容器环境),但是创建文件永远可行。我用过的很多云实例默认没有 swap,加一个 swapfile 是两三分钟的事。
- 透明且易管理。
ls -lh /swapfile就能看到文件占多大,swapon --show能看到生效情况。出问题大不了swapoff,不会像分区表那样留有残余结构。
有一点需要说明的坑:Btrfs 文件系统对 swap 文件支持不完整,内核文档明确写着 Btrfs 的 swap 文件不支持写时复制(CoW)和压缩。如果您的根分区是 Btrfs,要么手动关闭相关属性再建 swap 文件,要么干脆用 Zram 或者分一个小 swap 分区。XFS 和 ext4 都没有这个困扰,fallocate创建方便,mkswap直接就能用。
2. 创建 Swap 文件的完整流程
2.1 创建前的环境检查与规划
动手之前先把三件事摸清楚,不然后面会遇到各种莫名其妙的坑。
第一件事,看当前有没有 swap 以及具体状态:
swapon --show free -h cat /proc/meminfo | grep -i swap如果swapon --show输出为空,说明没有启用任何交换空间。free -h里的Swap行会显示总量和已用量。/proc/meminfo里的SwapTotal和SwapFree字段能帮你在脚本里快速判断。
第二件事,确认你的文件系统类型:
df -Th /根据我前面的经验,ext4 和 XFS 都可以放心用 swap 文件,Btrfs 需要额外处理。如果你连根分区是什么都不确定,这里一定要查一下。
第三件事,也是很多人容易忽略的——磁盘空间。Swap 文件是提前分配好的,不像普通文件那样按需增长,所以你得为它预留实实在在的空间。用df -h看看/的可用空间,SSD 一般建议至少留出物理内存的 20%~50% 作为 swap 容量。对 4GB 内存的机器,我通常创建 4GB swap;对 16GB 的机器,默认 8GB 足够大多数桌面或中小型服务器使用。数据库或 Java 堆比较大的场景,再酌情上调。
容量规划的经验公式有两个参考方向:
- 内存 ≤ 2GB 的设备:swap 可以设为内存的 2 倍(早期发行版默认也这么干)。
- 内存大于 2GB 的设备:swap 一般设为内存的 1 倍或略低即可,如果主要是桌面用途且有休眠需求,则尽量让 swap ≥ 内存大小(其实 sleep 时需要镜像能放下)。
这些不是金科玉律,具体还得结合你的实际负载来测。
2.2 创建 Swap 文件:两种方法选一种
创建 swap 文件有两种主流方式,fallocate和dd。我先把命令列出来,再说差异。
方法 A(推荐,ext4/XFS 上效率高):
sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile方法 B(Btrfs 或 fallocate 受限环境使用):
sudo dd if=/dev/zero of=/swapfile bs=1M count=4096 sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile看到区别了吗?fallocate是直接调用文件系统分配空间,速度极快,4GB 文件基本秒级完成,因为不实际写数据,只是“预订”空间。dd则是实打实往磁盘上写零字节,速度受磁盘写入能力限制,但兼容性最好。
有个细节要专门提醒:创建完 swap 文件之后,一定要执行chmod 600。原因在于mkswap工具会对这种对系统安全敏感的文件主动做权限检查,如果权限太宽松(默认 644),它可能会拒绝操作或者发出警告。同时,swap 文件本身可能包含上一次进程遗留下的敏感数据,用户可读意味着任何人都可能把里面的内容 dump 出来看。这个权限设置不是可选项,是必须项。
mkswap的默认行为还会把 swap 设备的 UUID 打印出来,这在我们后面做自动挂载和休眠配置时很有用,先记下它。
启用之后验证:
sudo swapon --show free -h正常情况下你会看到这个新加的 swap 文件已经生效。此时系统已经具备交换能力,但如果你重启机器,这个 swap 又会消失,除非我们把它写进/etc/fstab。
2.3 开机自动挂载与验证
重启之后 swap 文件不会自己出现,需要把它加进/etc/fstab。这一步的坑很多,我详细说一下。
方式一:用绝对路径配置,简洁直观。在/etc/fstab末尾添加:
/swapfile none swap sw 0 0四个字段的含义分别是:设备路径(/swapfile)、挂载点(swap 场景下用none)、文件系统类型(swap)、挂载选项(sw)、dump 标志(0)、fsck 顺序(0)。
方式二:用 UUID 配置,这个在存在多个 swap 文件或者需要精确控制时的场景下更稳。先用blkid查到 swap 文件的 UUID:
blkid /swapfile输出类似:
/dev/swapfile: UUID="4b9a7d9e-..." TYPE="swap"然后 fstab 写:
UUID=4b9a7d9e-... none swap sw 0 0我个人更推荐方案一(路径形式),因为 swap 文件名是自己起的、路径固定,UUID 也不是刚需,而且一旦重新创建 swap 文件,UUID 会变、fstab 里的写法还得跟着改,反而多出维护成本。UUID 的价值更多体现在 swap 分区或涉及多块磁盘的场景下,避免设备名漂移。
写完 fstab 别急着重启,先用sudo swapon -a手动验证配置能否解析并挂载。如果这里有报错,多半是 fstab 语法问题,趁早改掉。然后sudo reboot再验证swapon --show是否仍然显示。
提示:fstab 写错会导致开机时进入 emergency mode。只要语法按上面的格式写,基本不会出问题;万一踩坑了,开机提示输入 root 密码进入修复模式,注释掉问题行再重启即可。
验证没问题之后,一个基础的 swap 文件就建好了。但我要强调,这只是第一步,真正让 swap 用出价值的是后面的调优。
3. Swap 调优的关键参数
3.1 swappiness:内核有多“愿意”用 swap
你可能听过vm.swappiness这个参数,但未必了解它内部怎么工作。它的取值范围是 0 到 100,默认通常是 60。值越高,内核越倾向于把内存页换出到 swap;值越低,内核越倾向于回收 page cache 而不是内存页。这不是一个“开/关”的参数,而是一个“倾向”参数,具体数值影响的是内核页面回收算法里匿名页和缓存页的平衡点。
有一个常见的错误认知是:“把 swappiness 设为 0,swap 就不会被使用了”。千万别这么理解。swappiness=0只是让内核极度不愿意换出匿名内存页,但系统内存严重不足时照样会用 swap,只是优先级被放到最后。同样,设为 100 也不代表所有内存都立即进 swap——它只表示匿名页和缓存页被平等对待,回收时一视同仁。
具体场景怎么调,我的经验如下:
- 桌面开发机:建议
vm.swappiness=10,因为交互响应优先,不希望频繁把内存页搬到磁盘导致卡顿感。尤其你开着浏览器、IDE、终端,内存里有大量显示相关的冷数据页,低 swappiness 能让这些页尽量留在内存。 - 服务器(数据库、Redis 等):建议
vm.swappiness=1 或 10更合适,一般不建议设 0。数据库自己有缓存,内核过度参与 swap 反而会干扰它的内存管理;但完全没有 swap 的余地也不利于极端场景兜底。 - 内存非常紧张的机器(比如只有 1GB 内存的旧设备):建议保留默认值 60,甚至调高到 80~100。因为物理内存本来就不够,让内核更积极回收和交换反而能降低 OOM 概率。
临时调整:
sudo sysctl vm.swappiness=10查看当前值:
sysctl vm.swappiness cat /proc/sys/vm/swappiness想让设置永久生效,把参数写进配置文件,详见 3.3 节。
3.2 其他容易被忽略的内核参数
除了 swappiness,还有几个参数对 swap 的使用效率和系统的整体稳定性影响很大,值得放在一起说。
vm.vfs_cache_pressure。这个参数控制内核回收目录项(dentry)和 inode 缓存的倾向,默认值 100。数值越高,回收越激进;数值越低,内核越倾向于保留这些缓存。对于文件操作频繁的服务器(比如跑着 Nginx 或 GitLab),把vfs_cache_pressure调低到 50 左右可以降低目录查询的延迟。但要注意,设置过低会让 VFS 缓存占据太多内存,反而挤压 page cache 空间,所以要适度。
vm.min_free_kbytes。它表示系统保留最少可用物理内存的 KB 数。默认值内核会自动计算,但可以手动调高,防止在某些内存分配极端情况下内核陷入无内存可用的死锁状态。比如你发现系统在高负载下出现长时间的卡顿,可以先vmstat确认 free 量是否经常为 0,如果是,适当调高min_free_kbytes会有效果。这个值也不是越大越好,太大会浪费可用内存,导致应用可用的内存变少。
vm.overcommit_memory。这个参数控制内核是否允许进程申请超出物理内存+swap 总量的内存。默认值 0 表示启发式检查;值 1 表示总是允许超额申请;值 2 表示禁止超额并配合overcommit_ratio精确控制。生产环境跑数据库的机器我不建议轻易改这个,改了之后可能牺牲内存安全换性能,需要在充分测试后使用。
vm.swappiness 再补一个细节:内核版本比较老的系统(如 3.x),swappiness=0的实际行为可能比新版更激进,因为它把匿名页回收彻底禁止了,在某些情况下反而更容易触发 OOM。若非必要,新内核也不推荐设 0。
3.3 永久生效的配置方法与优先级
上面用sysctl -w做的修改在重启后会丢失。要永久化,在/etc/sysctl.conf或者/etc/sysctl.d/下新建一个专用的配置文件。我习惯把这类自定义参数集中放一个文件,方便审计和回溯,比如/etc/sysctl.d/99-swap-tuning.conf:
vm.swappiness=10 vm.vfs_cache_pressure=50写入后执行:
sudo sysctl --system这个命令会按/etc/sysctl.d/下的文件顺序加载所有配置。99-前缀保证它最后加载,避免被其他配置覆盖。如果想立刻验证:
sudo sysctl vm.swappiness如果输出vm.swappiness = 10,说明生效成功。有一点需要注意:有些发行版(比如某些最小化系统)没有/etc/sysctl.d目录,需要自己创建,权限要设 644。
提示:内核参数配置存在优先级差异。通过
sysctl -w的临时设置优先级最高,文件加载只是设定下一个启动周期的默认值。如果你想在生产环境临时调参又怕干扰,可以直接用sysctl -w观察,确认稳定之后再写入文件。
4. 监控与故障排查实战
4.1 怎么判断 swap 用得好不好
判断 swap 是否健康,不能只看free -h那一行。swap 使用率低不一定是好事,使用率高也不一定是坏事,关键是理解它的成因。
先用free -h看总体:
free -h重点关注avail这一列,这代表估算的可分配内存总量(包含可回收的缓存)。如果avail持续很低,而si/so两个统计值很大,说明系统正经受内存压力。
进一步用vmstat看交换活动:
vmstat 5输出的si(swap in)和so(swap out)字段,单位是 KB/s,它们反映的是“实际发生了多少交换 IO”。如果这两个值长期大于 0,说明物理内存确实不足,内核在频繁倒腾。这时候你该做的不是调 swappiness,而是加内存或者优化应用内存占用。
更深入一层,用/proc/meminfo判断内存压力下的细节:
cat /proc/meminfo | grep -E 'SwapTotal|SwapFree|Committed_AS|MemAvailable'如果Committed_AS远超MemTotal + SwapTotal,意味着系统有大量进程申请了内存但还没实际使用,这时启用 overcommit 策略可能会有意义。
对生产环境,sar -S是性能监控神器,能看到每个采样点的 swap 活动:
sar -S 1 5它能区分从磁盘读入内存(swap in)和从内存写入磁盘(swap out)的速率。我见过一些“内存明明够用但表现像缺内存”的机器,sar -S一眼就能看出问题其实出在不正常的 swap 抖动上——比如 swap 设备被其他 IO 阻塞。
4.2 常见故障场景与解决办法
场景一:swap 文件用着用着性能急剧下降,系统像被冻结。原因多半是 swap 文件所在的磁盘本身已经 IO 饱和,或者 swap 文件碎片化严重,导致随机读写延迟暴涨。解决思路:一是把 swap 文件挪到更快的设备(比如 NVMe SSD 而不是机械盘);二是考虑用 zram 方案,在压缩内存里做 swap,减少磁盘 IO 参与。zram 我自己在内存小的开发机上试过,内存压缩的优势在某些场景下非常明显,但它容量受物理内存限制,不适合作为大容量兜底。
场景二:swapon报错 “swapon failed: Invalid argument”。最常见的原因是文件系统类型不对,比如在 Btrfs 或某些网络文件系统上创建了 swap 文件。另一种可能是文件有洞(hole),fallocate创建的文件在某些文件系统下存在未分配的块,mkswap会拒绝。如果遇到这种,改用dd方式重建。
场景三:开机卡在 emergency mode。这个问题多半是/etc/fstab里 swap 配置出了问题,比如路径不存在或 UUID 改了。前提是你在重启前没做swapon -a的验证。进了 emergency mode 也别慌,用只读方式挂载根分区,编辑 fstab 注释掉问题行,重启后再仔细排查。
场景四:休眠失败(suspend to disk 不行)。如果你使用 swap 文件作为休眠目标,除了 fstab 配置之外,还需要让 initramfs 认识这个文件。做法是在/etc/default/grub里添加resume=/swapfile和resume_offset=参数,后者可以通过swap-offset /swapfile命令查到,然后运行sudo update-grub和sudo update-initramfs -u重建引导文件和 initramfs。这个流程每个发行版大同小异,Ubuntu/Debian 系这样操作没问题。
场景五:swap 用满导致系统卡顿到无法操作。即使在内存不足时,有些应用也会继续疯狂申请内存,最终把 swap 也耗尽,系统反复陷入内存回收循环。这种情况下,最快的应急手段是sudo systemctl restart重启相应的服务进程,或者手动释放一些缓存:sync && echo 3 > /proc/sys/vm/drop_caches。但注意,drop_caches 释放的是 page cache,不是 swap 中驻留的匿名内存页,对内存压力缓解有限。真正干净的方案是找出消耗大户,杀之或调整限额。
注意:不要在生产环境的未知机器上随意执行
echo 3 > /proc/sys/vm/drop_caches,这只适合你在完全清楚后果的情况下使用。我更推荐先定位占用内存的进程:ps aux --sort=-%mem | head -20,再决定下一步。
4.3 如何安全调整或删除 Swap
有时候你觉得 swap 文件太小了想加大,或者想彻底删掉,这个操作必须有顺序。乱来容易造成数据问题,虽然 swap 里的数据本身不需要保留,但操作中的失误可能导致系统不稳定。
第一步,先把交换空间里的数据搬回物理内存:
sudo swapoff /swapfile这一步会阻塞直到数据换出完成。如果物理内存不足以容纳 swap 中的内容,swapoff可能会卡住或者在日志里报错。这种情况下优先确保内存释放足够后再执行,或者考虑增加临时 swap(比如再建一个小的)来过渡。
第二步,删除文件:
sudo rm /swapfile第三步,清理/etc/fstab里对应的行。
如果你只是觉得文件大小不合适,先按上面的步骤把原 swap 撤掉,然后用第 2 节的方法重建一个合适大小的新文件即可。整个过程不影响系统运行(短暂内存压力略高,生产环境建议挑低峰时段操作)。
5. 综合配置建议
5.1 不同场景的推荐参数组合
我把实战中常用的参数配置整理成一个参考表,方便各位直接抄作业。注意这是经验值,不是绝对标准,落地之前请结合实际监控做微调。
| 场景 | swap 容量建议 | vm.swappiness | vm.vfs_cache_pressure | 说明 |
|---|---|---|---|---|
| 桌面开发机 / 办公机 | 内存的 1 倍(有休眠需求则≥内存) | 10 | 50 | 优先交互流畅,保留文件缓存 |
| 数据库服务器 | 内存的 0.5~1 倍 | 1~10 | 100(默认) | 避免内核过多干扰数据库缓冲池 |
| 内存小的嵌入式设备 / 树莓派 | 内存的 1.5~2 倍 | 60~100 | 100 | 物理内存太小时,让 swap 积极兜底 |
| 运行大量 Java 应用的服务器 | 内存的 0.5~1 倍 | 10 | 50 | Java 堆内存大,给 GC 留足物理内存 |
| 文件/缓存类服务器(Nginx 等) | 内存的 0.5~1 倍 | 10~20 | 50~75 | 保留多数 inode/dentry 缓存,加速文件访问 |
表格只是参考方向,真正的生产环境必须在负载压力下用vmstat观察si/so的变化,再对参数做针对性调整。
5.2 我的调优心路历程与总结建议
踩了不少坑之后,我的个人原则基本稳定为三条:
第一,有内存优先加内存,swap 是手段不是目的。如果你的应用经常把 swap 打满,加物理内存往往比调任何参数都直接有效。Swap 只是兜底机制,不应该是正常运转的主力。
第二,所有参数改动都要记录、可回滚。改内核参数前先在配置文件里注释原始值,改动后记录时间、改动内容和观察到的现象。你下次遇到类似问题排查时,这份记录能帮你迅速判断是哪个参数导致的。
第三,监控先行,调整随后。先把监控体系建起来,保证能查到历史数据,再动手调整。我个人的习惯是用vmstat定时记录到文件,再配合sar留历史。没有监控就调参,等于蒙着眼睛开车。
现在这篇文章望对大家排查 swap 相关的问题有帮助。最后分享一个小技巧:如果你用 systemd 管理服务,可以在服务的 unit 文件中加入MemoryHigh=和MemoryMax=限制内存上限,这样一来就少了很多触发 swap 的场景,比单纯调内核参数更能治本。关于 swap 文件、参数调整以及监控,内容差不多就是这些,欢迎大家留言交流实际的调优心得。