
1. Swap 到底在解决什么问题先把概念和取舍掰开讲做运维这几年被问得最多的一类问题就是“我这台机器的 Swap 到底该不该留”。问的人里有刚接手一台云服务器、free -h一敲发现 Swap 是 0 的新人也有被监控告警追着跑、发现凌晨三点 Swap 使用率飙到 90% 的老手。这个问题没有标准答案但如果连 Swap 是怎么工作的都没搞明白直接抄别人的swapoff -a那大概率是在给自己埋雷。Swap交换空间也叫交换分区本质上是内核在物理内存吃紧时把一部分“暂时用不上”的内存页挪到磁盘上的一块预留区域。挪出去的过程叫换出swap out需要时再读回来叫换入swap in。它和 Windows 里的“虚拟内存”是一回事只是 Windows 把分页文件和内存管理包装得更隐蔽Linux 则把控制权完整交到你手里——你可以决定它有多大、在哪块盘上、什么时候用、优先级多高甚至完全关掉。1.1 虚拟内存、Swap、物理内存三者的关系很多人把“虚拟内存”和“Swap”画等号这其实不准确。Linux 上每个进程看到的地址空间都是虚拟内存它由 MMU 通过页表映射到物理内存页帧上这套机制跟有没有 Swap 没关系。Swap 只是虚拟内存体系里的一个“后备仓库”当归档页匿名页比如 malloc 出来的堆、进程栈在物理内存里待不下时内核可以把它写到 Swap 里腾出物理页给别人用。所以判断一台机器“内存够不够”不能只看物理内存大小还要看工作集的常驻部分有多大。一个 8G 内存的机器跑一个 6G 常驻的 Java 应用Swap 设 2G 可能就够它扛过流量尖峰但如果是 8G 内存跑 16G 常驻的数据库Swap 设多大都救不了你只会把每次内存回收变成磁盘 IO 风暴。再补一个常被忽略的点文件页page cache不需要 Swap 也能回收脏页直接写回磁盘就行。真正依赖 Swap 的是匿名页。这也是为什么一台纯文件服务器把 Swap 关掉基本没啥感觉而一台跑着大量堆内存应用的机器关掉 SwapOOM Killer 会来得更快。1.2 为什么有人要禁用 Swap有人却离不开它禁用 Swap 的主流理由有三个都很实在。第一个是延迟可预测性。Swap 在 SSD 上的随机读写延迟大约是内存的几百到上千倍在一次请求链路里触发换入尾延迟P99、P999会直接炸掉。对延迟敏感的中间件、实时计算节点宁可让请求失败也不愿意让它慢慢换页。第二个是避免“假活”。有些服务在内存不足时会疯狂换页进程还在、端口还通、健康检查还过但每秒只能处理几个请求这种状态比直接挂掉更难排查。关掉 Swap 之后内存不够就是 OOM挂了就是挂了问题暴露得更干脆。第三个是特定软件的硬性要求。Kubernetes 从早期版本起就要求节点关闭 Swapkubelet 会检查failSwapOn不关就直接拒绝启动。Hadoop、部分分布式数据库的官方部署文档里也明确写了要关。反过来不能随便关 Swap 的场景同样明确内存本身就小的开发机、跑着 Elasticsearch 这类需要大量 mmap 的中间件、还有要支持休眠hibernate的笔记本。休眠要求 Swap 容量至少能装下整个内存镜像你把 Swap 删了systemctl hibernate直接报错。我的习惯是服务器按用途分类决策桌面机和个人开发机一律保留 Swap。桌面机上的浏览器、IDE、容器会瞬间把内存打满有 Swap 兜着至少不会正在写的代码被 OOM 干掉。1.3 先看现状查看 Swap 使用情况的几条命令动手之前先搞清楚现在是什么状态。别小看这一步我见过有人在没确认swapon --show输出的情况下直接mkswap把一块正在用的 Swap 分区重新格式化了结果就是内核直接懵掉。最常用的三条命令各有侧重# 1. 人类可读的内存概览看总量和已用量 free -h # 2. 专门列 Swap 设备能看到类型、大小、已用、优先级 swapon --show swapon -s # 老系统上更通用的写法 # 3. 内核视角的原始数据配合脚本解析很好用 cat /proc/swapsfree -h的输出里有两个字段容易被误读。buff/cache是文件缓存加上内核 slab这部分内存“用着但可回收”不需要紧张。available才是内核估算出来的“新起一个进程大概能拿到多少内存”它已经扣掉了不可回收部分判断内存是否真的吃紧看这一列比看free那一列靠谱得多。还有一个细节swapon --show如果没有任何输出说明当前系统一块 Swap 都没启用。这时候要区分两种情况——是压根没配还是配了但没启用。前者查/etc/fstab有没有 swap 条目后者看blkid里有没有TYPEswap的分区。想把使用强度也摸清楚可以加两条# 从 /proc/vmstat 看换入换出页数单位是页通常 4K grep -E pswpin|pswpout /proc/vmstat # 每 2 秒刷一次观察 si/so 列换入/换出单位 KB/s vmstat 2vmstat的si和so长时间非零才是真正需要关注的信号。偶尔跳一下几十 KB 属于正常抖动持续几百 KB/s 以上就说明内存压力已经很明显了。注意pswpin/pswpout是自开机以来的累计值看单次快照没意义要间隔采样对比。别拿一个累计十几天的数字去判断“现在是不是在换页”。2. 动手前的准备容量规划、形态选型和风险自查决定要动手之后大部分人的第一反应是“设多大”。这个问题在网上的答案五花八门有说内存两倍的有说固定 8G 的有说越大越好的。这些说法放在十年前可能在某种程度上成立但现在的硬件和内核已经有了更细的调优手段照抄容易踩坑。2.1 Swap 大小怎么定给一套能直接套用的规则传统经验值确实来自 RAM 两倍那个年代当时内存普遍 1G 到 2G内存本身昂贵用磁盘顶一顶性价比高。现在一台机器动辄 64G 内存再按两倍算就是 128G Swap纯属浪费磁盘。我更倾向于按“是否需要休眠”和“内存压力来源”两个维度来定场景建议 Swap 大小说明桌面/笔记本需要休眠物理内存的 1.0~1.5 倍内核休眠镜像会压缩但仍建议留足SSD 上还要考虑恢复速度桌面/笔记本不休眠4G~8G 固定值主要用来兜底浏览器和 IDE 的尖峰通用服务器内存 ≤ 4G2G~4G允许一定程度换页避免频繁 OOM通用服务器内存 8G~32G4G~8G 固定值超过 8G 收益递减重点是限制单次换页规模数据库、缓存、消息队列2G~4Gswappiness 调低只为应急不参与日常换页Kubernetes 节点0关闭kubelet 强制要求可用 zram 替代部分场景容器宿主内存敏感型4G~8G配合 cgroup 限制防止某个容器把整机内存拖垮固定值的思路是Swap 的定位是“缓冲带”不是“扩容盘”。它的作用是让内核有喘息空间去回收可回收页、让 OOM Killer 有更准确的判断依据而不是让你把 8G 内存的机器当 16G 用。给 4G 到 8G足够扛过一次短时的内存尖峰给 128G只会让系统在慢速换页中慢慢僵死。还有个经常被忽略的容量维度单次换出规模。内核回收内存时按 LRU 链表扫描Swap 空间足够大时它可能一次性换出几百 MB 的匿名页造成明显的 IO 尖峰。有些场景下反而是“Swap 小一点”能起到断路器的作用——空间用满之后内核只能转向直接回收direct reclaim甚至触发 OOM问题暴露得更早。2.2 分区 Swap、文件 Swap、zram 三种形态怎么选确定要多大之后下一个问题是“用什么形式”。主流有三种各自的适用边界很清楚。Swap 分区是传统做法从磁盘划一块独立分区mkswap格式化后启用。优点是 IO 路径最短没有文件系统层开销性能最稳缺点是调整大小要动分区表扩容得删了重建在已经上线的机器上操作风险高。适合装机时一次性规划好的物理机。Swap 文件是现在更推荐的方案。在现有文件系统上创建一个普通文件mkswap之后当 Swap 用。优点是灵活扩容就是swapoff、加文件、swapon三步不用碰分区表缺点是有文件系统开销而且在不同文件系统上有不同的坑下面细说。绝大多数云主机、容器宿主用的都是这种。zram是另一条路它不是往磁盘写而是在内存里划一块区域做压缩块设备。写入的数据先压缩再存压缩比通常在 2:1 到 4:1 之间。好处是速度快因为不发磁盘 IO坏处是它吃的是物理内存属于“用 CPU 换内存”。树莓派、ChromeOS、Android、Fedora 默认都用 zram。内存 2G 以下的机器、或者 IO 慢但 CPU 空闲的设备zram 收益非常明显。# 看看内核是否支持 zram modinfo zram 2/dev/null | head -n 3 # 看系统里有没有已经建好的 zram 设备 lsblk | grep -i zram形态性能扩容难度是否需要磁盘空间典型场景Swap 分区最优高是物理机、装机规划Swap 文件良好低是云主机、虚拟机、临时扩容zram最好无磁盘 IO中否占内存小内存设备、只读根文件系统我自己的选择逻辑是树莓派、路由器、小内存 VPS 优先上 zram普通云主机用 Swap 文件装机时就分好的物理机沿用分区。三种可以共存靠优先级区分先后使用顺序。2.3 开工前的备份和风险自查清单改 Swap 属于“影响面看起来小、实际可能很大”的操作尤其是涉及swapoff的时候。我踩过一次够呛的坑在一台内存使用率 85% 的机器上直接swapoff -a因为要换更大的 Swap 文件结果内核得把所有已换出的页读回物理内存本来剩 15% 内存根本装不下当场触发 OOM把线上服务干掉一个。所以每次动手前我一定跑这套检查确认可用内存是否大于已用 Swap。free -h里available的值必须大于 Swap 的used值留出至少 20% 余量。不满足就先扩容内存或者分批停服务。确认目标磁盘空间。df -h看清楚要放 Swap 文件的挂载点的剩余空间还要留意 inode 情况df -i。确认文件系统类型。df -T或者mount | grep 挂载点。ext4 基本无坑XFS 和 btrfs 有特殊要求见第 3 章。备份/etc/fstab。这是最容易被改坏的文件改错一行可能导致系统起不来。cp /etc/fstab /etc/fstab.bak.$(date %F)只要一秒钟能救你半小时。确认是否在容器里。容器内的 Swap 行为受宿主控制/proc/swaps在容器内可能显示宿主的数据别被误导。确认有没有监控告警。换 Swap 期间如果触发内存告警提前跟值班同学打个招呼省得半夜被电话叫醒。提示涉及swapoff的操作尽量放在业务低峰期做。如果实在无法停机改用“先加新的、再停旧的”这个顺序swapon新文件 → 降低旧 Swap 优先级 → 观察压力 →swapoff旧的。这样内存里始终有可用的换出空间。3. 创建并启用 Swap 的完整实操流程准备工作做完进正题。这一章把“从零加一块 Swap”的完整链路走一遍两条路线Swap 文件推荐和 Swap 分区。每一步我都会说明为什么要这么写以及不这么写会出什么问题。3.1 路线一用 Swap 文件创建推荐最灵活假设要给/分区加一块 8G 的 Swap 文件路径定在/swapfile。命令顺序很重要别跳步。# 1. 创建文件分配 8G 的块不写零快 sudo fallocate -l 8G /swapfile # 如果 fallocate 不可用或不生效退回 dd # sudo dd if/dev/zero of/swapfile bs1M count8192 statusprogress # 2. 立刻锁权限只有 root 能读写 sudo chmod 600 /swapfile sudo chown root:root /swapfile # 3. 格式化成 Swap 格式 sudo mkswap /swapfile # 4. 启用 sudo swapon /swapfile # 5. 验证 swapon --show free -h这里有几个点值得展开说。关于fallocate和dd。fallocate只是把块标记为已分配不实际写数据所以秒完成。但它在某些文件系统上会导致 Swap 文件“看起来有洞”swapon时会报swapon failed: Invalid argument。判断方法是fallocate之后检查文件是否连续分配——用filefrag -v /swapfile看输出如果 extent 的 physical offset 出现大量空洞或者有unwritten标记就得改用dd。我现在的习惯是fallocate之后直接swapon失败就换dd两分钟内能搞定。关于权限600。这不是可选项。Swap 文件里可能残留进程的敏感数据权限放开就是信息泄露。内核也会校验权限不对swapon会直接拒绝并提示insecure permissions 0644。关于mkswap的输出。它会打印一段 UUID 和最后一行的Setting up swapspace version 1, size 8 GiB。如果 size 明显小于预期说明文件分配有问题回到上一步检查。关于 btrfs 和 XFS 的特殊处理。btrfs 上创建 Swap 文件必须先关掉 COW# btrfs 专用先建空文件、关 COW、再分配 sudo truncate -s 0 /swapfile sudo chattr C /swapfile sudo fallocate -l 8G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfileXFS 相对简单但要注意内核版本。老内核3.x 早期在 XFS 上对 Swap 文件的支持不完善fallocate出来的文件无法启用直接用dd最稳。另外 XFS 上的 Swap 文件大小上限受文件系统块大小和内核版本限制超大 Swap比如 64G 以上建议改用分区。关于 zram 的创建方式简单贴一下供参考sudo modprobe zram # 创建 4G 的 zram 设备压缩算法用 lz4 echo lz4 | sudo tee /sys/block/zram0/comp_algorithm echo 4G | sudo tee /sys/block/zram0/disksize sudo mkswap /dev/zram0 sudo swapon -p 100 /dev/zram0 # 优先级给高优先用它zram 的优先级的思路是“先用快的那块”所以给-p 100物理 Swap 文件给-p 10。这样内核优先在 zram 里压缩存储满了才往磁盘写。3.2 路线二用独立分区创建 Swap云主机很少用这个路线但物理机装机或者磁盘重新规划时会遇到。核心是分区表的操作风险比建文件高一个量级因为分区一改相邻分区的起始位置可能被挪动数据就没了。安全顺序是先备份数据再用fdisk或gdisk划出分区改分区表之后立刻partprobe让内核重读最后才mkswap。# 以 /dev/sdb 为例假设要划一块 8G 的 Swap 分区 sudo fdisk /dev/sdb # 交互过程 # n - 新建分区 # p - 主分区 # 回车 - 默认起始扇区 # 8G - 大小 # t - 改类型 # 82 - Linux swapGPT 下用 19 # w - 写入并退出 # 让内核重新读取分区表不需要重启 sudo partprobe /dev/sdb # 或者用这个更保险 sudo partx -a /dev/sdb # 格式化并启用 sudo mkswap /dev/sdb1 sudo swapon /dev/sdb1如果分区表是 GPT且系统用 systemd 管理建议顺手改一下分区类型 UUID让 systemd 能识别# GPT 下把分区标记为 Linux swap sudo sgdisk --typecode1:8200 /dev/sdb这里有个经验能不动分区表就不动。真正需要独立分区 Swap 的场景只有“装机时预先规划”和“根文件系统是只读的嵌入式设备”。已经上线的机器扩容 Swap用文件方案能省掉 90% 的风险。另外一个细节mkswap有个-L参数可以打标签对多块 Swap 的场景很有用sudo mkswap -L SWAP_DATA /dev/sdb1 # 后续 fstab 里可以用 LABELSWAP_DATA 引用3.3 写 fstab 实现开机自动挂载顺手调 swappiness上面swapon启用的 Swap 是临时的重启就没了。持久化要写/etc/fstab。改之前先备份sudo cp /etc/fstab /etc/fstab.bak.$(date %F-%H%M)然后追加一行。推荐用 UUID 而不是设备路径因为设备名/dev/sdb1可能因为插拔顺序、内核枚举变化而漂移UUID 是分区本身固有的。# 先拿到 UUID sudo blkid /swapfile # 输出类似/swapfile: TYPEswap UUIDa1b2c3d4-... # 也可以用 mkswap 重新生成并打印 UUID sudo mkswap -U random /swapfile写入/etc/fstab格式是六列# file system mount point type options dump pass UUIDa1b2c3d4-... none swap sw,pri10 0 0六列的含义分别是设备标识、挂载点Swap 固定写none、文件系统类型、挂载选项、dump 备份标志Swap 填 0、fsck 检查顺序Swap 填 0。pri10是优先级。如果有多块 Swap数字大的先用。这一点在多盘机器上很有价值——把快的 NVMe 盘上那块 Swap 优先级设高慢的机械盘设低。写完别急着重启用mount -a或者swapon -a验证语法# 先全部关掉再按 fstab 重新启用能过就是语法没问题 sudo swapoff -a sudo swapon -a swapon --show注意swapoff -a有内存风险执行前务必确认free -h里的 available 大于 Swap used。如果你在远程 SSH 会话里操作一旦触发 OOM 连不上机器只能走控制台。接下来调swappiness。这个参数控制内核换出匿名页的积极程度取值 0-100。默认值各发行版不一样RHEL 系一般是 30Ubuntu 桌面是 60。数值越高越倾向于把进程内存换出去给文件缓存腾地方。# 临时改立即生效重启失效 sudo sysctl -w vm.swappiness10 # 永久改 echo vm.swappiness10 | sudo tee /etc/sysctl.d/99-swappiness.conf sudo sysctl -p /etc/sysctl.d/99-swappiness.conf # 验证 cat /proc/sys/vm/swappiness关于怎么取值我的经验是场景swappiness理由桌面/笔记本60默认允许积极换出保住前台应用的响应通用服务器10尽量不换但保留应急能力数据库/缓存1只在极端压力下换出K8s 节点0配合关闭 Swap已关 Swap 时该参数无意义小内存设备zram100鼓励换出到 zram压缩后释放物理内存这里有个常见的误解swappiness0不等于完全不换页。内核在内存极度紧张、无法回收足够页框时仍然会换出匿名页这是硬性行为。所以别指望着设成 0 就能让延迟彻底稳定真要彻底禁用还是得swapoff。再加一个配套参数vm.vfs_cache_pressure默认 100。它控制内核对 dentry 和 inode 缓存的回收倾向调高比如 200会更快回收目录项缓存降低内存长期占用代价是文件访问变慢。内存小的机器上我会设成 200 试试效果因负载而异。4. 修改已有 Swap 的大小和优先级实际运维里更常见的是“改”而不是“建”。扩容、缩容、调整优先级这一章把三种操作拆开讲清楚。4.1 扩容把 2G 的 Swap 扩到 8G 的完整流程最安全的做法不是原地改文件大小而是新建一个大文件、启用、再删掉旧文件。原因很简单swapoff旧文件时要把它承载的页全部读回内存这一步本身就有 OOM 风险而原地调整大小并不能绕过这一步。完整流程如下假设旧 Swap 是/swapfile2G要扩到 8G# 1. 看当前压力确认能安全做 swapoff free -h swapon --show # 2. 新申请 6G 的空间不需要 8G因为可以复用一部分 sudo fallocate -l 6G /swapfile.new sudo chmod 600 /swapfile.new sudo chown root:root /swapfile.new sudo mkswap /swapfile.new # 3. 先启用新的给个更低的优先级让内核先用旧的 sudo swapon -p 5 /swapfile.new swapon --show # 4. 观察几分钟确认内存没被压垮 vmstat 5 12 # 5. 停掉旧的 sudo swapoff /swapfile # 6. 删除旧文件把新文件改名占位 sudo rm /swapfile sudo mv /swapfile.new /swapfile最后一步要特别注意改名之后 UUID 没变UUID 存在 Swap 头里跟路径无关所以/etc/fstab里的 UUID 条目不用改。但如果 fstab 里写的是路径/swapfile那就得确认路径一致否则开机swapon -a会失败。验证sudo swapon -a 21 swapon --show free -h还有一种偷懒做法直接用dd追加到现有文件后面sudo swapoff /swapfile sudo dd if/dev/zero of/swapfile bs1M count6144 oflagappend convnotrunc sudo mkswap /swapfile sudo swapon /swapfile这个方式看起来简洁但有个隐患mkswap会重写 Swap 头如果中途断电文件就废了。而且它依然没绕过swapoff时的内存风险。所以只在“内存充裕的小机器”上用别在生产环境图省事。4.2 缩容和删除顺序错了会出大问题删除 Swap 的正确顺序永远只有一条swapoff从/etc/fstab里删掉对应条目删除文件或分区顺序错在前两步的话重启后系统会尝试挂载一个不存在的 Swapswapon -a报错系统进入紧急模式emergency mode那场面就很尴尬了。# 1. 停用 sudo swapoff /swapfile # 如果有多块想全停 # sudo swapoff -a # 2. 编辑 fstab 删掉那一行 sudo vi /etc/fstab # 确认没有残留 grep -i swap /etc/fstab # 3. 删文件 sudo rm -f /swapfile # 4. 验证 swapon --show # 应该没输出 free -h如果确实搞坏了 fstab 导致进不去系统补救路径是在 GRUB 菜单按e编辑启动参数在linux那行末尾加systemd.unitemergency.target或init/bin/bash进去之后把 fstab 改回来。这个操作我做过两次第二次是因为复制粘贴多打了一个空格。缩容的操作跟扩容反着来先swapoff删掉文件重建一个更小的再swapon。缩容前一定要确认内存余量够因为swapoff会把已换出的页全部拉回内存。如果 available 不够宁可先加内存再缩 Swap别硬来。4.3 多块 Swap 的优先级策略和分布多块 Swap 共存时内核按优先级从高到低使用。相同优先级的会做条带化round-robin相当于变相提速。查看优先级swapon --showNAME,TYPE,SIZE,USED,PRIO # 或者 cat /proc/swaps设置优先级有两个途径# 临时命令行指定 sudo swapon -p 100 /dev/zram0 # zram 给最高 sudo swapon -p 20 /swapfile.ssd # SSD 上的文件 sudo swapon -p 5 /swapfile.hdd # 机械盘垫底 # 永久写进 fstab 的 options 列 # UUIDxxx none swap sw,pri100 0 0我在一台 32G 内存的构建机上做过这样的分层zram 8Gpri100 NVMe Swap 文件 8Gpri20 SAS 盘 Swap 分区 16Gpri5。构建任务突发内存峰值时先用 zram压缩存储几乎不损失速度zram 满了才溢出到 NVMeSAS 盘基本用不上属于兜底。整体构建时间比单层 Swap 缩短了大约 15%磁盘写入量也明显下降。提示相同优先级的多块 Swap 会做条带化理论上能提升吞吐但也会放大单块盘故障的影响面。生产环境如果想用这个特性确保两块盘在不同物理设备上别在同一个 RAID 组的两个逻辑卷上做。另外Swap 不要放在网络文件系统上。NFS、CIFS 上的 Swap 文件内核根本不支持swapon会直接报错。也别放在软 RAID 上的文件系统里做 Swap 文件IO 路径太长延迟不可控。要用 RAID就用 RAID 上的分区做 Swap。5. 关闭 Swap临时禁用、永久禁用和关闭后的补坑前面说过关 Swap 是个决策不是个操作。但既然要做就得做干净不能只敲一句swapoff -a就完事。这一章把三种关法讲清楚再把关闭之后必须补的坑一次性列全。5.1 swapoff 的几种姿势分别用在什么场景swapoff的行为差异很大取决于你给什么参数、以及当时的内存状况。# 关掉指定的一个 sudo swapoff /swapfile # 关掉全部 sudo swapoff -a # 关掉 /etc/fstab 里定义的所有 Swap sudo swapoff --allswapoff -a在内存紧张的机器上是高危操作。它会把所有已换出的页读回内存而这个读回过程是同步的期间进程会卡住。如果读回量超过了可用内存内核只能启动 OOM Killer 挑进程杀。所以在内存使用率超过 70% 的机器上我宁愿一个接一个地关中间加个观察窗口for dev in $(swapon --showNAME --noheadings); do echo disabling $dev ... sudo swapoff $dev free -h sleep 10 done如果swapoff卡住不动了大概率就是在疯狂换入。这时候别 CtrlC 强断因为中断可能导致部分页状态不一致。要么等它跑完要么从另一个终端看vmstat 1确认si列在掉、说明在推进。实在不行准备好重启。一个小技巧swapoff期间临时把vm.swappiness改成 0能让内核尽量不产生新的换出加快整个过程。5.2 永久禁用的三种落地方式只敲swapoff -a是临时的重启之后 Swap 又回来了。要让禁用真正生效得同时处理几个地方。方式一注释 fstab 条目。最传统、最稳妥。sudo vi /etc/fstab # 找到 swap 那行行首加 # #UUIDxxx none swap sw 0 0 # 验证语法确保不会因为格式问题进紧急模式 sudo findmnt --verify --verbose方式二用 systemd 的 swap 单元屏蔽。现代发行版systemd 239会把 fstab 里的 Swap 自动转换成.swap单元。直接屏蔽比改 fstab 更干净。# 查看有哪些 swap 单元 systemctl list-units --typeswap --all # 停掉并屏蔽 sudo systemctl stop swapfile.swap sudo systemctl mask swapfile.swap # 确认 systemctl status swapfile.swapmask会创建一个指向/dev/null的符号链接任何试图启动它的操作都会失败。适合“我确定这台机器永远不要 Swap”的场景。方式三内核启动参数。在 GRUB 里加swapaccount0并不能禁用 Swap别被误导。正确的做法是确保没有resume参数指向 Swap 分区同时保证 fstab 和 systemd 里都没有 Swap 定义。单纯靠启动参数关 Swap 并不可靠还是得回到前两种方式。改完 GRUB 相关配置之后记得sudo update-grub # Debian/Ubuntu 系 sudo grub2-mkconfig -o /boot/grub2/grub.cfg # RHEL 系重启后验证swapon --show # 空输出 free -h # Swap 行全 0 cat /proc/swaps # 只有表头5.3 关掉 Swap 之后必须补的几个坑这一步是很多人漏掉的。关 Swap 不是终点它会牵动一串周边配置。坑一OOM Killer 变得更激进。没有 Swap 作为缓冲内存耗尽时内核只能杀进程。要调整 OOM 保护策略# 看某个进程的 OOM 评分数值越高越容易被杀 cat /proc/$(pgrep -f mysqld | head -1)/oom_score # 保护关键进程范围 -1000 到 1000-1000 表示几乎不会被杀 echo -500 | sudo tee /proc/$(pgrep -f mysqld | head -1)/oom_score_adjsystemd 服务可以在 unit 文件里写OOMScoreAdjust-500比手动改更持久。坑二休眠功能失效。关掉 Swap 之后再执行systemctl hibernate会报Failed to hibernate system via logind: Not enough swap space for hibernation。如果你确实要用休眠就不能关 Swap而且 Swap 容量要够大。折中方案是保留一块专用的小 Swap 只用于休眠通过resume参数指向它业务层面用swappiness0让它平时不参与换页。坑三Kubernetes 节点还要改 kubelet 配置。光是关掉 Swap 不够kubelet 的failSwapOn默认是 true某些版本需要显式确认。检查/var/lib/kubelet/config.yamlfailSwapOn: true memorySwap: swapBehavior: NoSwap改完重启 kubeletsudo systemctl restart kubelet sudo systemctl status kubelet journalctl -u kubelet -n 50 --no-pager | grep -i swap坑四容器内存限制的语义变了。Docker 和 containerd 早期版本不支持给容器设 Swap 限制容器能用的 Swap 等于宿主的 Swap。宿主关掉 Swap 之后容器的内存上限就是硬上限超了直接 OOM kill不会再往下滑。这其实是好事但要让业务方知道做好内存水位监控。坑五监控告警要跟着改。原来基于“Swap 使用率”的告警会全部失效因为恒为 0。要换成基于available内存、pgmajfault主缺页次数、以及容器内存使用率的告警。# 主缺页次数从 /proc/vmstat 看反映真实的物理页加载 grep pgmajfault /proc/vmstat # 每个 cgroup 的内存事件统计cgroup v2 cat /sys/fs/cgroup/path/memory.eventsmemory.events里的oom_kill计数是判断容器是否被杀的黄金指标比看日志靠谱。6. 常见问题与排查技巧实录写了这么多原理和步骤最后落到实战。这一章是我这些年攒下来的问题清单按“看到什么现象 → 怎么定位 → 怎么解决”组织可以直接当速查表用。6.1 典型报错速查表出错的时候人会慌慌的时候容易乱敲命令越搞越糟。下面这些报错我都遇到过多遍对着表格先定位再动手。报错信息根本原因解决办法swapon: /swapfile: read swap header failed文件没mkswap或者被fallocate打出空洞重新mkswapfallocate出问题就换成ddswapon: /swapfile: insecure permissions 0644, 0600 suggested权限太松chmod 600 /swapfileswapon: /swapfile: swapon failed: Invalid argumentbtrfs/XFS 上的 COW 或 extent 问题btrfs 先chattr CXFS 改用dd创建swapon: /dev/sdb1: Device or resource busy分区已经被挂载或占用lsblk确认用途别误删数据分区swapoff: /swapfile: swapoff failed: Cannot allocate memory可用内存装不下已换出的页先加内存或停服务分批swapoff或直接重启systemd[1]: Failed to activate swap /swapfilefstab 里的 UUID 或路径不对blkid对照findmnt --verify检查语法Failed to hibernate: Not enough swap spaceSwap 容量小于内存扩容 Swap 到内存的 1.0~1.5 倍zram: Cannot allocate memoryzram 设备创建时物理内存已不足降低 zram 大小或先释放内存再操作补一个不在表里但很常见的现象free -h里 Swap 的 used 一直是 0但系统明显变慢。这时候别怀疑 Swap去查文件 IO。可能是 page cache 抖动、或者某个进程在做频繁的小文件读写。用pidstat -d 1或者iostat -xz 1看磁盘利用率。6.2 定位“到底是谁在用 Swap”Swap 使用率高了第一反应肯定是“哪个进程干的”。但 Linux 没有一个开箱即用的命令能直接按进程列出 Swap 占用量因为 Swap 是页级别的进程和页的对应关系在内核里暴露得不直接。最直接的方法是遍历/proc/*/statusfor pid in /proc/[0-9]*; do p${pid#/proc/} name$(grep -m1 ^Name: $pid/status 2/dev/null | awk {print $2}) swap$(grep -m1 ^VmSwap: $pid/status 2/dev/null | awk {print $2}) [ -n $swap ] [ $swap -gt 0 ] echo $swap kB PID$p $name done | sort -rn | head -20这行脚本会按 Swap 占用从大到小列出前 20 个进程。VmSwap字段单位是 kB注意换算。跑的时候如果有权限问题加sudo。更方便的是smem如果系统里有装# 按 PSS/USS 和 Swap 排序显示进程 smem -t -k -c pid user command swap uss psssmem的 Swap 列就是每个进程的 Swap 占用比手写脚本省事还能显示 PSS比例集大小这种更准确的内存指标。Ubuntu 上apt install smemRHEL 上dnf install smem。还有一个场景是容器里的进程。容器的/proc是否能看到宿主的进程取决于 PID namespace 配置。如果容器里看不到就得在宿主上用crictl ps或docker top定位容器再去读宿主的/proc。定位到进程之后的处理思路如果是一个内存泄漏的服务重启能缓解但要修代码如果是配置问题比如 JVM 堆设太大改配置如果是流量突增导致的考虑扩容或加限流。千万别人为地在高 Swap 占用时重启服务重启瞬间会触发大量换入可能把机器拖垮。真要重启先swapoff一块一块地停前提是内存够或者用滚动重启降低压力。6.3 几处实战中攒下的经验和坑最后这部分是我自己的私房笔记都是踩过的坑换来的网上大部分教程不会提。第一别迷信“Swap 一定要设成内存两倍”。内存 64G 的机器设 128G Swap除了浪费磁盘还会让内核在内存回收时更“大胆”地换出因为它觉得有空间。结果是长期运行在换页状态性能持续下降而不报警。固定 4G~8G 加低swappiness是我现在的主流配置。第二云主机的 Swap 效果和本地盘差异巨大。同样标称 SSD 的云盘随机小 IO 的延迟可能是本地 NVMe 的十倍以上。在云主机上Swap 更应该定位成“防 OOM 的最后一道锁”而不是性能手段。给 2G~4Gswappiness设 1让它基本不参与日常换页这个配置在大多数云环境里表现最稳。第三改 fstab 之后一定要findmnt --verify。这个命令会解析 fstab 并检查语法、UUID 是否存在、挂载点是否有效比人工眼里检查靠谱得多。我现在的流程固定是改 fstab →findmnt --verify --verbose→swapon -a或mount -a→ 重启验证。四步少一步都可能翻车。第四zram 在小内存 VPS 上的收益超出预期。一台 1G 内存跑静态站点和小数据库的 VPS加 512M zram压缩比约 3:1相当于 1.5G 逻辑容量在流量峰值时的响应时间明显改善而且磁盘 IO 几乎为零。配置也很简单几条echo命令就搞定。唯一要注意的是 zram 吃 CPUCPU 已经跑满的机器上收益会打折扣。第五swapoff -a之前先看available这条规则怎么强调都不为过。我在前文提了三次是因为它真的很重要。一个检查动作只要两秒钟能避免一次线上事故。养成肌肉记忆之后free -h是每次碰 Swap 的第一个动作比ls还顺手。第六Swap 文件的路径别放在/tmp或者会被 tmpfs 覆盖的挂载点下。tmpfs 是内存文件系统你在上面建 Swap 文件等于用内存模拟磁盘再拿来当内存用逻辑上来回绕了一圈实际可用容量反而变小。老老实实放/或者专门的swap挂载点/swapfile这个路径是大多数发行版文档的惯例就随大流。第七多台机器做批量化的时候把 Swap 配置写进配置管理。Ansible 里用一个 task 搞定创建、格式化、fstab、sysctl 四件事比手工 SSH 上去敲靠谱得多。下面这段是我常用的 Ansible 片段思路供参考- name: 创建 swap 文件 command: fallocate -l 4G /swapfile args: creates: /swapfile - name: 设置权限 file: path: /swapfile mode: 0600 owner: root group: root - name: 格式化为 swap command: mkswap /swapfile register: mkswap_result changed_when: Setting up swapspace in mkswap_result.stdout - name: 启用 swap command: swapon /swapfile when: mkswap_result.changed - name: 写入 fstab lineinfile: path: /etc/fstab line: /swapfile none swap sw,pri10 0 0 state: present - name: 设置 swappiness sysctl: name: vm.swappiness value: 10 sysctl_file: /etc/sysctl.d/99-swappiness.conf reload: yes用 Ansible 的好处不只是省事更重要的是幂等。creates和changed_when保证重复执行不会把已经正常的 Swap 又格式化一遍这在批量运维里是刚需。关于 Swap 的话题能挖的细节其实还有很多比如 cgroup v2 下怎么给单个容器限制 Swap、memory.swap.max和memory.max的配合逻辑、cgroup v1 和 v2 在统计口径上的差异。这些内容跟具体的内核版本和容器运行时强相关写在这里反而容易误导人。如果你在这几个方向上遇到了具体问题建议先用uname -r和cat /sys/fs/cgroup/cgroup.controllers确认自己处在哪套体系里再去找对应的文档比照着通用教程瞎调要靠谱得多。