在Linux服务器上摸爬滚打这么多年,swap这块我踩过的坑绝对能排进Top 5。刚接手线上机器那会儿,我一度以为swap就是“用磁盘假装内存”,能不开就不开——直到一次深夜线上告警,Redis进程因为内存耗尽直接挂了,我才真正意识到swap在操作系统内存管理里的分量。今天借这个机会,把swap分区的创建与使用一次性说透,重点讲清楚两种主流方法:传统swap分区和swap文件。不管你是刚装完虚拟机的新手,还是维护着几十台生产环境的运维,这篇文章都能给你一个可以直接照着做的完整方案。
1. 先从本质说起:swap到底是什么,为什么Linux离不开它
1.1 内核的“临时仓库”:swap的工作原理
把swap理解成办公桌上的抽屉就对了:内存是桌面,你正在处理的东西必须摊在桌面上,但房间只有这么大,东西堆满之后要么扔进垃圾桶(清掉进程),要么塞进抽屉(swap),要用的时候再翻出来。Linux内核里的页面回收机制(page reclaim)就是把不活跃的内存页挪到磁盘上,腾出空间给真正热的数据,整个过程由kswapd这个内核线程默默完成。
很多初学者对swap有个误解,觉得它就是个“慢速内存”,甚至认为有swap就意味着性能差。实际上,Linux内核的内存管理是分层的:hot pages留在物理内存,cold pages被换出到swap。当系统内存有富余时,swap基本是闲置的,只有内存压力上来,swap才开始替物理内存分担压力。换句话说,swap不是用来提升速度的,它是内存的“安全垫”,避免内存瞬间冲高时系统直接崩掉。
还有一个容易忽略的点:swap不仅能换出匿名页(进程堆栈数据),还能配合内存回收机制做页缓存(page cache)管理。在kswapd眼里,物理内存的水位线(watermark)决定了一切——当空闲内存低于low水位线,后台回收开始工作;低于min水位线,进程的页面分配就会被阻塞,这时候如果一直得不到缓解,内核就直接把OOM Killer拉出来点名了。
1.2 没有swap会发生什么,也就是为什么我不建议你关掉它
网上有不少“内存够大就要关swap”的说法,我强烈建议你谨慎跟风。内存再大的机器也有峰值的,尤其是跑数据库、Java应用、构建任务的时候,瞬时内存开销能把你的物理内存瞬间打穿。没有swap兜底,内核就只能走极端——OOM Killer。
OOM Killer的逻辑是挑选“最该杀”的进程杀掉,评分标准包括内存占用、运行时间、进程优先级等等,可它在多进程生产环境里并不总能做出“正确”选择。我见过一台没有swap的机器,内存被Java进程和文件缓存挤爆,结果OOM Killer反而把nginx给杀了,业务直接断流。有swap的情况下,系统还能勉强维持响应,至少给你介入处理留出时间。
当然,过度依赖swap也是不行的。总内存+swap被写满之后,系统依然会OOM,而且以swap为主的磁盘I/O风暴会让整个系统卡到让人绝望,这时别慌,按第5章的方法处理即可。一句话:内存充足时swap是摆设,内存紧张时swap是救命的,但别把swap当成扩容内存的手段。
1.3 swap分区和swap文件:两种载体怎么选,各有啥优劣
Linux创建swap有两条路:一是划一块独立分区,二是直接用文件系统里的一个文件来做swap。这两种方式在功能上没有本质区别,底层都是块设备读写,但使用体验差异明显。
| 对比项 | swap分区 | swap文件 |
|---|---|---|
| 性能 | 直接读写块设备,少量开销 | 经过文件系统层,略有开销 |
| 灵活性 | 分区建完大小难改 | 随时增删,无需重新分区 |
| 适用场景 | 物理机、传统部署 | 虚拟机、云主机、容器 |
| 管理难度 | 分区表操作,相对复杂 | 几个命令搞定,适合新手 |
| 开机自动挂载 | 通过fstab或传统的挂载配置 | 同样支持fstab |
| 特殊情况 | 分区被占用时需谨慎操作 | 磁盘格式需支持fallocate |
我个人的倾向是:物理服务器、传统机房环境,用swap分区更稳,毕竟独立分区不会受文件系统碎片和权限影响;云上、虚拟机、容器环境,用swap文件更灵活,随时能改大小、能清理,不用重新走一遍分区流程。接下来的内容,两种方法我都会带各位完整走一遍。
2. 方法一:传统swap分区,适合物理服务器和多系统环境
2.1 实操前准备:看懂磁盘布局再动手,别把数据盘毁了
创建swap分区之前,先看清楚机器当前的磁盘结构,这是新手最容易出事的环节。我强烈建议先用lsblk看一眼整体布局:
lsblk输出里能看到vda、sda之类的磁盘设备,以及下面挂载的分区。接着用fdisk进到目标磁盘交互界面,p参数可以打印分区表,确认要操作哪块盘。划swap分区不一定非要用一整块盘,可以在空闲空间里划一个主分区,也可以直接用独立的整个磁盘,方式是灵活的。
需要注意,swap分区的大小规划直接关系到后期体验。老规矩“物理内存的1~2倍”其实已经不太适合大内存机器了——你一台256G内存的数据库服务器,难道划512G磁盘给swap?合理做法是看业务:桌面和开发机建议给物理内存的1倍左右,云上通用服务器给2~8G做兜底即可,数据库或Java重内存应用可以按物理内存的50%~100%预留。我自己的经验是:先按业务峰值评估,宁可刚开始少分,也别分太多挤占磁盘空间,因为swap分区后期扩容很麻烦。
2.2 用fdisk创建分区并设置类型:82号类型别记错
假设我现在要对/dev/sdb这块空盘划分区:
fdisk /dev/sdb交互界面里依次输入:
n:新建分区,按提示一路回车,把整个磁盘作为一个分区t:修改分区类型,输入分区号(通常就是1),再输入代码82,这是Linux swap的十六进制标识p:打印确认一下分区类型显示为“Linux swap”w:写入并退出
很多教程在这儿就结束了,但我要多提一句:fdisk写完之后要确认内核是否已经识别到新分区,比较保险的方法是运行partprobe /dev/sdb刷新分区表,特殊情况下甚至需要重启才能让系统拿到分区信息。这一步在热扩容场景特别重要。
2.3 mkswap格式化与swapon立即启用
分区创建好之后,下面这几个命令就是核心三条龙了:
mkswap /dev/sdb1 swapon /dev/sdb1 free -h第一条mkswap负责把分区初始化为swap文件系统,注意它输出的UUID信息先记下来,后面写fstab会用到。第二条swapon是让swap立即生效,这时free -h里能看到Swap那一行的总量发生变化,说明系统已经使用上这块swap了。
如果这步操作的是已有分区而不是新盘,一定要先确认分区是空数据或者已经备份,mkswap等于格式化,数据直接被覆盖,没有后悔药。生产环境我一般还会先执行swapoff确保之前的swap资源先释放,避免部分进程引用旧swap引发报错,但日常创建全新分区没这个必要。
2.4 开机自动挂载:fstab到底怎么填才不会踩坑
做完上面几步,重启系统后swap信息就没了,因为还没配置持久化。Linux开机挂载的核心是/etc/fstab文件,需要把swap分区的信息写进去:
vim /etc/fstab在文件末尾加一行:
/dev/sdb1 none swap sw 0 0这里有个教训:强烈建议别直接写设备名,而是用UUID。因为设备名在重启后可能变化(比如内核识别顺序变了),UUID是固定的,能避免开机时fstab找不到设备导致的启动问题。先用blkid查看分区的UUID,再写进fstab:
UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx none swap sw 0 0写完fstab绝对不能急着重启。先跑一遍swapon -a试试,这个命令会按fstab配置把所有swap挂载起来,检查输出有没有报错。我在实际排障中发现,很多莫名其妙的启动失败都是fstab某一行格式错了,趁早验证比让机器在重启时卡住强得多。
3. 方法二:swap文件,虚拟机和云主机场景的灵活解法
3.1 为什么云环境我更推荐swap文件
现在云主机和虚拟机场景,我几乎很少用swap分区了,原因很实际:云主机的系统盘分区通常已经规划好,想从里面再挖一块独立分区出来,扩容和调整都受云平台限制;而swap文件就是一个普通文件,想要多大就多大,不想要了直接删除,完全不影响磁盘分区结构。
如果用的是LVM逻辑卷,也可以用lvcreate创建逻辑卷做swap,但灵活性依然不如swap文件。尤其是容器场景,很多基础镜像为了精简,压根没有预先创建swap分区,这时候用swap文件几乎是唯一选项。另外,swap文件还适合那种“临时加个几G的swap顶一下峰值”的救急场景,几分钟就能搞定,验证完就可以撤。
3.2 5分钟创建一个swap文件:从dd到swapon全流程
在root权限下,创建swap文件非常简单,一共四条命令:
dd if=/dev/zero of=/swapfile bs=1M count=2048 chmod 600 /swapfile mkswap /swapfile swapon /swapfile第一条命令生成一个大小为2048MB(2G)的文件,bs=1M表示每次写入1M,count=2048表示写2048次。这里有人爱用fallocate -l 2G /swapfile,速度确实快很多,但我实测过在一些文件系统(比如老版本xfs)上,fallocate出来的文件可能带有空洞,mkswap会拒绝或写入时出问题,所以求稳就用dd,虽然慢一点但绝对不踩坑。
chmod 600是为了限制文件权限,这点非常重要——swap文件里可能残留内存中的敏感数据,如果不限制普通用户可读,就存在信息泄露风险。第四步执行之后再用free -h验证,看到swap总量增加就成功了。
3.3 开机自动挂载和swap文件大小自由调整
和swap分区一样,swap文件也需要写fstab才能开机自动挂载,注意这里不能写UUID(文件没有稳定的UUID),直接写路径即可:
/swapfile none swap sw 0 0因为路径是固定的,只要文件还在,重启就不会出问题。写完依然建议执行swapon -a验证一遍。
swap文件最大的优势就是扩容灵活。假设我2G的swap文件不够用了,想扩到4G,标准的操作序列是:
swapoff /swapfile rm -f /swapfile dd if=/dev/zero of=/swapfile bs=1M count=4096 chmod 600 /swapfile mkswap /swapfile swapon /swapfile注意swapoff这步不能省略,文件正在被内核使用的时候直接删会出问题。如果是临时扩容不想破坏原文件,也可以再建一个新文件,两个swap文件或分区同时swapon是完全支持的,Linux允许你同时挂载多个swap设备。
4. 调优与监控:swap不只是创建就完事
4.1 swappiness参数:决定系统“多积极地使用swap”
系统创建好swap之后,还有一个关键参数要认识:vm.swappiness,取值范围0~100,默认通常是60。这个值越高,内核就越优先把内存页换到swap;越低说明系统越倾向于复用物理内存,尽量少用swap。
很多网上的性能优化教程都会建议“把swappiness改成10甚至0”,强调充分利用物理内存。这个观点本质上没错,但不要无脑照搬。真正的思路是结合场景:
- 桌面Linux建议保留默认60,兼顾交互流畅和内存释放
- 内存充足的普通服务器可以设10左右,减少不必要的磁盘I/O
- 数据库、缓存类服务建议设成10以下甚至0,数据能留内存就留内存
- 内存偏小、又跑着批量任务的机器建议50以上,防止内存瞬间打满
临时调整用:
sysctl vm.swappiness=10永久生效则写入/etc/sysctl.conf:
vm.swappiness=10然后执行sysctl -p加载配置。这里我要纠正一个常见认知偏差:swappiness设置为0不代表绝不使用swap,它只是降低内核换出内存页的倾向,当内存压力极大时,内核依然会调用swap兜底。
4.2 日常监控:内存够不够、swap执行了多少,用这几条命令快速定位
swap创建完不是放着不管了,尤其是生产环境,我每隔一段时间都会看一下系统内存和swap的状态。最常用的组合是:
free -h:看总内存、已用、剩余以及swap总量、已用,最直观vmstat 1:每秒刷新一次,看si和so列,这两个值表示从swap换入换出的数据量,如果持续很大,说明内存压力已经到了值得关注的程度swapon -s:列出当前所有swap设备和各自的使用情况,排障时一眼看出哪个swap设备在起作用top/htop:按字母键M按内存排序,能看到各进程的RES和SWAP列,判断哪个进程在大量消耗内存
还有个进阶命令sar -S,可以查看历史swap使用情况的统计,适合做周期性巡检。这块内容虽然基础,但很多线上故障都栽在“平时不看,爆了才知”——养成固定巡检习惯,比任何调优技巧都重要。
4.3 常见问题速查表:从挂载失败到性能暴跌
下面这些是我在实际使用中最常遇到的问题,整理成一张速查表,方便大家直接对照排查。
| 现象 | 原因 | 解决思路 |
|---|---|---|
| 开机后swap没生效 | fstab配置错误或UUID不对 | 检查fstab格式,执行swapon -a验证 |
| swapoff提示“swapoff failed” | 系统当前内存实在不够,无法一次性回收 | 设置vm.swappiness调高,或降负载后再操作 |
| 创建swap文件时mkswap报错 | 文件含空洞或权限不对 | 用dd替代fallocate重新生成文件,检查权限 |
| 磁盘I/O突然飙高 | swap频繁换入换出,内存压力过大 | 查看vmstat的si/so,定位进程,扩容内存或swap |
| OOM Killer随机杀进程 | swap也满了,系统彻底无法分配内存 | 紧急清理内存占用,必要时加swap文件救急 |
| fstab里写设备名导致启动失败 | 设备路径重启后变化 | 改用UUID(分区)或路径(文件) |
5. 实操复盘:我亲历的swap事故与维护心得
5.1 案例:swap分区用得好好的,一次重启后“失踪”了
之前帮一台物理机换内核,重启之后登录上去发现系统卡到不能自理,free -h一看swap整个没了。查了半天,问题就出在fstab里写的设备路径/dev/sda3,而新内核环境下盘符顺序变了,系统没能按老路径挂载上swap。这个例子再次印证了用UUID替代设备路径的必要性。当时修复也很快:blkid查出swap分区的UUID,更新fstab,swapon -a手动挂载,系统立刻恢复正常。
这个事故给我留下的习惯是:每次配完swap,都要在fstab里写下设备UUID并当场验证;更换内核或硬件之后,第一件事就是检查fstab和swap状态,防患于未然。
5.2 案例:swap文件太多导致根分区吃紧
还有一次,某台机器根分区使用率莫名飙到90%,排查半天才发现是前一个同事创建的8G swap文件还在,而新加的16G swap分区并没有被替换掉。原来他当时的操作只做了重新mkswap和swapon,忘了swapoff老的swap文件,导致老的swap一直没释放。
多个swap设备同时挂载本身没问题,但要有清晰的规划和管理意识。我现在的习惯是:每次开新swap,先swapoff旧设备、在fstab里清理对应行,再用swapon -s确认当前生效的swap设备只有预期的那几个。这样可以有效避免磁盘空间被不透明的swap文件长期占用。
5.3 案例:Redis频繁OOM,swap成为了最后一根稻草
处理过的一次线上故障让印象最深刻:一台跑着Redis的内存型机器,物理内存32G,起初运维为了方便加了个16G的swap文件,系统内存压力上来时大量冷数据被换到swap,导致Redis处理请求出现很大的延迟尖刺。
后来诊断发现,对延迟敏感的应用来说,换出无可避免,但被换出页面再次被访问时的代价极大。因此对Redis这类应用,我最终的建议是:不碰swap文件的物理层,而是把vm.swappiness调到极低(甚至临时改vm.overcommit_memory和tcp_keepalive相关参数配合),从应用层保证热数据留在内存里,同时尽量避免OOM。这个案例说明:swap是保底手段,但不是性能优化工具,二者一定要分清。
5.4 维护经验小结
经历过这些事故之后,我给自己定了几条维护swap的约定,也分享给各位参考:
- 每次创建swap后,一定要写fstab并执行swapon -a验证
- 观察swap使用率,持续高占用优先排查内存压力,而不是无限扩大swap
- swap文件定期检查权限和磁盘剩余空间,防止根分区被吃满
- 对延迟敏感的业务,用swappiness等参数来控制swap的使用倾向,而不是简单删除swap
- 维护记录里明确标注swap的创建时间和大小,方便后期变更时清理旧配置
这些经验就像系安全带,平时觉得多余,事故来了才知道有多救命。每当有人问我swap该怎么配,我都会说:创建swap其实只需要几条命令,真正能体现功力的地方在于对内存、swap和业务三者平衡的理解。