1. 什么时候真的需要动swap分区:先看清现状再下手
先说个我自己的经历。去年有一台跑数据分析的ubuntu服务器,内存只有8G,但数据集加载起来动不动就吃掉15G,结果就是训练到一半系统开始卡顿,free命令一看swap占用早就满了,紧接着OOM Killer直接把我训练进程杀了。当时手头没有物理内存条可加,最快能救火的办法就是扩容swap分区。
但在这之前,必须提醒你:不是所有卡顿都该靠扩容swap解决。swap本质是内存的“溢出缓冲区”,它只能缓解内存压力,不能提升程序运行速度。如果机器内存长期被占满、swap一直在高频读写,那你的瓶颈其实是内存容量本身,扩容swap只是拖延方案。它的适用场景很明确:
- 内存不够但暂时买不了新内存条,比如仍在保修的笔记本、远程云服务器。
- 内存使用有瞬间峰值,比如编译大型项目、跑数据处理任务,峰值过去后内存会回落,这种场景swap能接住。
- 休眠功能需要swap(hibernation模式下内存镜像要写入swap)。
- 防止某些进程突然吃满内存导致整个系统OOM崩溃,此时swap能兜底。
看当前状况用这几条命令:
bash复制# 查看内存和swap总体情况
free -h
# 查看每个进程的内存占用,判断谁在吃内存
top -o %MEM
# 查看当前swap分区的设备名和挂载状况
swapon --show
以我那次为例,free -h输出显示Mem total 7.6G,Swap total 2.0G,Swap used 2.0G。swapon --show显示的swap设备是/dev/sda2,类型partition。这就说明两块信息:第一,我现在swap配额太少;第二,swap是独立分区。这两个信息决定了我接下来扩容用哪种方案。
扩容换分区有两条常见路线:直接改swap分区的大小,或者新建一个swap文件让系统用。改分区本身要动磁盘分区表,涉及数据风险和复杂的顺序调整;而swap文件方案灵活、不用动分区表,随时创建随时删,非常适合应急场景。我实际用的就是swapon + swapfile的组合方案,下文会完整说明。
另外一个判断前置条件:做之前必须确认根目录或者你打算放swap文件的那个文件系统有足够剩余空间。执行df -h看看,比如根目录如果还剩18G,而你只想扩4G swap,完全没问题。空间不够的话,扩容swap文件也不成立,只能另想办法。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. swap的运作原理与分区、文件两种形态的取舍逻辑
很多人只记住了扩容命令,没搞懂swap本身的工作机制,结果就是遇到问题时无从下手排查。这里把核心原理讲清楚,后面出了问题你才知道怎么定位。
2.1 swap在内存管理中扮演的角色
先把概念简化成生活场景。你家的书桌(物理内存RAM)写满了作业,桌面放不下了,作业本会移到旁边的储物架(swap分区)上,要用的时候再拿回来。书桌越小,作业本来回移动就越频繁,写作业的效率自然就低。
Linux内核通过页管理(page)和缺页中断(page fault)机制来完成这个交换过程。当物理内存不足时,内核的内存回收机制会挑出一些不常访问的匿名页面(例如进程堆栈数据、映射的内存数据等),先写入swap再释放对应RAM。真正有程序需要这些数据时会触发缺页异常,数据再被换回内存。整个过程由内核的kswapd进程掌控,它利用swappiness参数决定自己“多勤快”地把内存内容往swap里赶。
你可以用cat /proc/sys/vm/swappiness查看当前系统的swappiness值。默认通常是60,取值范围0到100。数值越高,内核越积极使用swap;越低则越倾向先清缓存,实在不够了才动用swap。
有个细节要说明:文件页(file-backed pages,即普通磁盘文件的缓存数据)清出内存时是直接丢弃(因为文件本身还在磁盘里),不需要写swap;只有匿名页(anonymous pages)在被驱逐时,为了不丢数据,才必须写到swap。这就是为什么有swap数据交换时,你的磁盘读写IO会明显升高。理解了这一点,你就明白为什么我查问题时,重点看swap读写速率和系统负载的关联。
2.2 swap分区 vs swap文件:实际选择要看场景
在ubuntu上扩容,首先面临的就是选分区还是选文件。我整理了一张对比帮你快速决策:
| 对比项 | swap分区 | swap文件 |
|---|---|---|
| 创建/扩容难度 | 需要用fdisk/parted调整分区表,操作复杂 | 一条dd命令创建文件,随时扩大或缩小 |
| 是否需要重挂载或重启 | 部分方案需要重启才生效 | 创建好直接swapon激活,无需重启 |
| 对已有数据的影响 | 高风险,误操作可能影响临近分区 | 无风险,只要目标文件系统有空间 |
| 性能表现 | 连续磁盘块,顺序IO性能更好 | 存在文件系统碎片,但实际差异通常不明显 |
| 适用场景 | 装机规划磁盘时就预留了分区 | 临时扩容、虚拟机镜像文件、应急场景 |
我个人的倾向很明确:除了新建系统时顺手预留一个swap分区,日常扩容一律用swap文件。原因很实在——不用求人、不用冒险动分区表、随时可以反悔。
真实场景里,swap分区的顺序写入性能比swap文件好一点,但如果系统是SSD,这种差距几乎感知不到。机械硬盘上差距会大一些,但用swap文件的便利性依然值得。我身边很多运维同事实际也都是用swap文件方案,至少在线扩容时没人愿意碰分区表。
2.3 关于LVM:如果在用逻辑卷,扩容有更优雅的方式
如果你安装ubuntu时启用了LVM(Logical Volume Manager,逻辑卷管理),那还有一条更顺滑的路:直接对swap逻辑卷扩容。用lvdisplay检查一下,如果swap是逻辑卷,扩展步骤就变成了:
bash复制# 关闭swap
sudo swapoff /dev/mapper/ubuntu--vg-swap
# 扩展逻辑卷(假设原来4G,扩展到8G)
sudo lvextend -L 8G /dev/mapper/ubuntu--vg-swap
# 重新格式化为swap并启用
sudo mkswap /dev/mapper/ubuntu--vg-swap
sudo swapon /dev/mapper/ubuntu--vg-swap
这种方式不需要新建文件,也不需要改分区表,是最稳妥的方案。不过对绝大多数家用机器来说,安装时未必配置了LVM,swap文件方案依然是通用性最强的办法。本文后续主要围绕swap文件展开,但逻辑和思路是一致的。
3. 扩容实操全流程:用swap文件把swap从2G扩到16G
下面是我实测过的完整流程。以扩容到16G为例,你的机器可以根据实际内存和磁盘剩余空间调整数字。
3.1 第一步:确认磁盘剩余空间并关闭现有swap
bash复制# 查看磁盘使用情况
df -h
# 查看当前swap信息和设备名
free -h
swapon --show
如果确认空间足够,先临时关闭当前swap。这一步很重要,不关掉现有swap,新文件激活后会同时存在多个swap设备,系统会按优先级使用,旧swap依然占着配额。
bash复制# 关闭所有swap设备
sudo swapoff -a
关闭后运行free -h确认Swap全部变为0。这里有个细节:如果系统内存压力很大,关闭swap这个动作可能临时导致内存紧张,所以建议在系统相对空闲的时间操作,并且确认free显示的内存有足够余量。
3.2 第二步:创建目标大小的swap文件
用fallocate还是dd创建swap文件,这个选择有讲究。
bash复制# 创建16G空文件
sudo fallocate -l 16G /swapfile
fallocate速度快,因为它是分配磁盘空间但不写实际数据。但有个坑:部分文件系统(如早期的某些overlayfs、部分网络挂载)不支持fallocate,会报"fallocate failed: Operation not supported";另外如果用fallocate创建的文件做swap,某些内核版本会提示"swapfile has holes",拒绝启用,因为它内部实现机制导致文件空洞中的物理块可能实际并未分配。
稳妥但不慢的方案是先fallocate,如果启用时遇到上述错误,再用dd兜底:
bash复制# dd方式创建16G文件
sudo dd if=/dev/zero of=/swapfile bs=1M count=16384 status=progress
命令含义:把/dev/zero(零字节流)以1M为单位块大小连续写16384个块,正好16G。dd虽然逐块写真实数据,但一次性到位,后续不会出问题。
3.3 第三步:设置安全权限
这一步千万别跳。swap文件里存的都是进程内存换出的数据,可能有明文密码、密钥之类的敏感信息。如果权限太松,任何普通用户都能读取这个文件,等于把系统所有进程的“内存裸奔”展示给别人。正确做法是权限改为600(仅root可读写):
bash复制sudo chmod 600 /swapfile
你可以检查一下:ls -lh /swapfile,权限部分应该显示-rw-------。
3.4 第四步:标记为swap并激活
bash复制# 格式化为swap格式
sudo mkswap /swapfile
# 激活
sudo swapon /swapfile
mkswap会输出SUCCESS字样,并显示swap版本和大小。static在激活成功后再检查一次:
bash复制swapon --show
free -h
两处输出的swap都应显示为16G。到此当前会话的扩容已经完成,但还没完,重启后会消失,必须写入开机自动挂载配置。
3.5 第五步:写入/etc/fstab实现开机自动启用
编辑fstab配置文件:
bash复制sudo nano /etc/fstab
在文件末尾追加一行:
text复制/swapfile none swap sw 0 0
这行配置含义依次是:设备路径 /swapfile;挂载点 none(swap没有显式挂载点);文件系统类型 swap;挂载选项 sw;dump标志0(不备份);fsck检查顺序0(不检查)。
如果之前fstab里已有旧的swap分区条目(比如/dev/sda2 none swap sw 0 0),注意确认是否需要删除或注释掉。旧条目指向的设备如果不再作为swap使用,开机时会提示找不到设备,虽然多数情况下系统会在启动过程中跳过,但谁知道会不会带来奇怪问题。我建议:既然现在统一用swap文件,就把旧条目注释掉,写#号开头即可。
3.6 第六步:重启验证
重启前最好检查配置文件语法是否正确,可以执行:
bash复制sudo findmnt --verify --verbose
它会逐行分析fstab,指出格式错误。没问题后再重启:
bash复制sudo reboot
重启后重新检查swapon --show和free -h。如果swap仍然是16G,配置就成功了。
4. 在这台机器上我踩过的三个坑
老实说,我最初动手扩容时并不顺利,连续踩了三个坑,每个都可以复现,写出来希望对你有用。
4.1 坑一:fstab写到swap分区的UUID导致开机失败
我在另一台机器上用的是swap分区,当时没有改用swap文件,而是试图直接扩展现有swap分区。我把fstab里的挂载写法改成了/dev/sda2后重启,系统直接进入emergency mode,屏幕提示找不到UUID对应的设备。
排查链路如下:登录后执行journalctl -xb看到systemd-fstab-generator报错"Failed to parse ...",然后检查blkid发现那个分区UUID变了——因为扩容重做mkswap后,swap分区的UUID会被重新生成,而老fstab里写死了旧的UUID。
解决办法:用blkid获取新的UUID并更新fstab,或者干脆在fstab里用/dev/sda2这样的设备路径,但设备路径在增删硬盘后可能变化(比如从sda变成sdb),所以用UUID其实是更稳的方式,关键是扩容后别忘了同步UUID。
这里特别提醒:fstab配置但凡有一行格式错误,可能导致整个系统启动卡住。所以改完务必用findmnt --verify验证,然后还可以执行sudo mount -a测试当前挂载配置能否正确应用(swap虽然不挂载,但这条命令能提前暴露出格式错误)。
4.2 坑二:紧急情况下用swapoff -a导致OOM
最开始为了扩容,我在内存几乎全满的状态下执行了swapoff -a,结果系统反应近乎卡死,最后OOM Killer开始杀进程。
原因其实在上面提到过:swapoff要求内核把所有swap内的数据回收回物理内存。如果物理内存余量不足以容纳swap中已使用的数据,内核只能强行把部分内存页换回,此时内存压力飙升,系统的正常功能都会受影响。
处理办法是分步降低swap使用量,或者干脆先清理内存占用。当时我使用的方法:先查top找出大内存进程,手动kill掉测试用的进程释放内存,再执行swapoff。更平滑的方式是用swapoff /dev/sda2只停掉单个设备,保留其他swap,减少一次性冲击。
实践建议:运行swapoff前先检查当前swap的使用量,如果swap占用超过了空闲物理内存,要么先清理内存,要么分多次关闭不同swap设备。最好不要赌系统能扛住瞬间内存压力。
4.3 坑三:mkswap后误操作把swap文件写进根目录导致根分区紧张
这个坑有点低级但值得说。当时建了一个很大的swapfile放在/(根目录)下,没意识到根目录剩余空间本来就不多。mkswap + swapon激活后,df -h显示根分区使用率飙到99%,系统出现"no space left on device",日志写不进去,某些服务直接不正常。
排查过程倒是简单:df -h一眼看到/swapfile占了绝大部分空间,但真正的问题是swap文件是否真的需要这么大、能否换个位置。后来我把swapfile移到了单独挂载的数据盘上。如果你根目录空间紧张,不妨放在一个挂载数据目录中,比如/data/swapfile,fstab里写对应路径即可。
需要补充的是:swap文件不宜放在被频繁写入的快照目录或网络文件系统(NFS)上,因为NFS网络延迟会导致swap读写阻塞,快照机制可能让swap极易堆积成大量重复数据。
5. 性能调优与日常监控:扩容后如何让swap真正好用
扩容完成后,并不代表一劳永逸。还要考虑swappiness参数、使用量的合理范围,以及监控手段。
5.1 swappiness值的实践调节
默认60适合大部分桌面用途,但对服务器或开发机,我更倾向于把它降低。原因在于:默认值会让内核过于积极地把稍久未访问的内存页移到swap,而把这些页重新换回内存的开销,在某些工作负载下比直接缓存还高。
两种典型调法:
bash复制# 临时设置为10,立即生效
sudo sysctl vm.swappiness=10
# 永久生效,写入sysctl配置
echo "vm.swappiness=10" | sudo tee /etc/sysctl.d/99-swappiness.conf
如果是数据库服务器、自建GitLab这类常驻内存型服务,我一般调到10。普通桌面如果内存有16G以上,也可以设到10。若你的机器内存很小(比如4G),可以保留默认60甚至更高,因为内存本身不够,内核积极换出反而能避免OOM。
5.2 swap的冷热状态与io延迟
swap并不是“不分青红皂白”都用。内核会记录页面活跃度(参考/proc/[pid]/smaps中的Swap字段),尽量先换出冷页面。但你仍然能通过io延迟直观感受swappiness的影响。如果发现某段时间磁盘写IO明显增加且无业务原因,多半是swap在换页,可以查看:
bash复制# 查看swap读写统计
cat /proc/vmstat | grep -E "pswpin|pswpout"
# 查看单个进程的swap占用
for file in /proc/[0-9]*/smaps; do awk '/Swap:/ {sum+=$2} END {print FILENAME, sum/1024 " MB"}' $file; done | sort -k2 -rn | head
pswpin是换入页数,pswpout是换出页数。如果pswpout持续大幅增长,说明内核在持续写swap,内存压力明显,需要关注是否需要继续扩容swap,或者升级物理内存。
5.3 监控:如何发现swap不够的早期信号
一到监控就是老生常谈,但这里确实有必要说几句可落地的。一个简单脚本,每5分钟记录一次内存和swap:
bash复制#!/bin/bash
while true; do
free -h >> /var/log/swap_monitor.log
date >> /var/log/swap_monitor.log
sleep 300
done
免责声明一下:这脚本没有加守护进程,你可以借助systemd service或cron实现定时执行。我自己的习惯是直接在终端用watch -n 5 free -h临时观察,再通过Zabbix之类的监控工具设置swap使用率超过80%告警。**告警阈值80%是我个人的常用值,你可以根据业务容忍度调整。**比如业务对延迟极度敏感,这个阈值就得调低。
5.4 如何安全移除不再需要的swap
如果你后来加了物理内存,不再需要16G swap,可以按这个顺序回退:
bash复制# 1. 停用swap文件
sudo swapoff /swapfile
# 2. 从fstab中删除对应行
# 3. 删除文件释放空间
sudo rm /swapfile
但注意:如果系统内还有其他swap设备或文件,swapoff只会关掉指定的这个,系统依然能运转。如果你的唯一swap文件被移除且无其他swap,系统在理论上是可以在不启用swap的情况下运行的,只是内存超载时风险更高。一般我建议至少保留2G左右的swap做兜底。
6. 常见疑问与踩坑后的个人心得
下面集中回答一些我经常被问到的问题,也分享一些很个人化的经验。
6.1 究竟设置多大swap才合理?
这个没有统一答案,但有一个实用参考线:
- 内存8G以下:swap设置为内存大小的1~2倍。
- 内存8G~32G:swap设置为8G~16G。
- 内存32G以上:swap通常8G就够,除非你明确要休眠(hibernate),那swap至少要与内存大小相当。
- 服务器跑数据库/K8s等:这类型系统更看重稳定性,一般保留4G~8G,防止极端内存需求下进程被杀。
我自己的操作习惯是:个人开发机开到内存大小的50%~100%,服务器在8G到16G之间。关键不是精确值,而是“够缓冲突发内存压力,但不至于占用太多磁盘空间”。
6.2 修改swap对现有程序有影响吗?
只要进程不主动读取已经被换出的页面,扩容过程本身对现有程序几乎没有影响。swapoff期间涉及大量换页可能让程序出现短暂的IO阻塞,如上面坑二所述。扩容完成后,由于swap配额变大,已运行进程的行为基本不会被改变,内核调度依然透明地使用swap。担心的话,最稳妥的方法是选业务低谷期操作。
6.3 为什么我的swap使用率总是很低,是不是白扩了?
swap使用率低是好事。绝大多数情况下我们希望swap长期低占用,这说明物理内存足够。swap更多是“安全垫”,平时用不到,关键时刻能救命。别因为“不用可惜”就刻意提高swappiness,那反而会降低系统性能。
6.4 我的个人体会
几次扩容操作下来,我的感受是:swap扩容本身不难,难的是在操作前做足判断,在操作后备好回退方案。 我自己现在遇到内存紧张,第一反应是先看free和top,弄清楚是瞬时峰值还是持续占用,再决定扩swap还是优化应用。另外,fstab这种东西改完一定要验证,别省那几十秒,不然真到重启时才发现问题,耽误的就是整个服务的时间。
建议你实际操作时,把每步命令的输出都截图或留存到日志里,特别是swapoff前后的free输出。出了问题能快速回溯,也能在写经验分享时给出真实数据做对比。
7. 扩展思考:swap扩容之外还可以做哪些事
如果你已经顺利扩容完swap,恭喜你,应急问题解决了。不过从运维角度说,内存压力管理不该只停留在swap维度,以下几点是我在其他环境中验证过的思路,一并分享:
7.1 zswap与内存压缩
ubuntu内核普遍支持zswap。它本质上是给swap加一层压缩缓存,内存压力大时先把页面压缩存储,减少实际写入swap的次数。对SSD寿命也是一种保护。启用方式因内核模块和GRUB参数而异,如果读者有兴趣,我可以后续单独写一篇zswap的调优内容。这里只说结论:它对降低swap写入量确实有帮助,但如果物理内存实在不够,zswap顶多是不让写入太频繁,解决不了根本短缺。
7.2 排查应用内存泄漏比扩容swap更治本
如果某个进程的swap占用以肉眼可见速度上升且从不回落,很可能就是内存泄漏。cgroup的memory limit可以防止单进程吃掉整个系统内存,systemd启动的service加MemoryMax配置就是一种简单的限流手段。经常有“swap永远在涨”的用户问我要不要继续扩容,我一般先建议用ps和smaps定位到具体进程,检查日志确认是不是泄漏——扩容只是给漏水的桶加水,堵漏才是正道。
7.3 磁盘性能对swap的影响
看到swap文件在机械硬盘上,我会强烈建议考虑关闭swap或尽量少见地用它。机械硬盘随机读写的延迟和固态硬盘相差一个数量级,大量swap换页会造成系统长时间无响应。如果你所有磁盘都是机械硬盘,也可以把swap放在磁盘最外圈(读写速度更高的区域),但现实中这优先级通常不高,优先加大物理内存才实在。
以上这些不一定立刻用得上,但和swap扩容一起纳入日常系统调优的考虑范围,处理问题时会更从容。希望这篇经验能帮你顺利完成ubuntu的swap扩容。
