一开始接触 Linux 服务器的时候,我对 Swap 的态度基本就是“系统默认给多少就用多少”,从来不主动去碰。直到有一次线上数据库服务器在业务高峰期直接卡死,排查下来才发现 Swap 分区只有默认的 2G,而物理内存已经加到 32G,内存一吃紧系统就开始疯狂换页,整个服务像是被按下了慢放键。那之后我就养成了一个习惯:每台新服务器上线前,先把 Swap 大小按业务场景调到位。但手动操作一套流程下来又是swapoff又是重建分区,稍微不留神还会把/etc/fstab搞坏,于是就有了今天这个 Swap 大小一键调整脚本。
这篇文章适合所有被 Swap 坑过的运维同行,也适合刚接触 Linux 系统的初学者。我会把 Swap 调整背后的原理、脚本的完整实现、每一行代码的用意,以及我在实际操作中踩过的坑全部写出来,保证你看完可以直接把脚本拿去用,也能根据自己的服务器情况灵活改。
1. 为什么需要一键调整 Swap 脚本
1.1 Swap 到底是做什么的
Swap 在 Linux 里的角色,简单说就是内存的后备队。当物理内存(RAM)被占满或者使用率过高时,系统会把一部分不常用的内存页挪到磁盘上的 Swap 空间里,腾出物理内存给当前急需的进程用。这个机制的好处是内存不会因为瞬时压力直接耗尽,坏处也很明显——磁盘的速度和内存完全不是一个量级,一旦系统开始频繁使用 Swap,应用的响应时间会显著变长。
在实际运维中我发现一个很普遍的误区:很多人以为 Swap 越大越好。结果就是磁盘被划出一大块 Swap 区域,真实场景下根本用不到,白占空间;另一部分人则把vm.swappiness调成 0,以为彻底禁用 Swap 就能提升性能,结果高并发时 OOM(内存耗尽)直接把关键进程杀掉,损失反而更大。合理的做法是结合物理内存大小、业务负载特征、磁盘类型这几个因素,给出一个相对科学的 Swap 大小,而这正是脚本要解决的核心问题。
1.2 什么场景下需要调整 Swap
我总结了几类常见场景,你可以对照自己的情况判断是否有调整 Swap 的需求:
- 物理内存从 16G 升级到 64G,但 Swap 还是安装系统时默认的 2G,导致偶发性卡顿。
- 建站初期随手给了个 4G 交换分区,后来业务改成数据库服务,内存消耗大幅上升,Swap 明显不够用。
- 买的是云服务器,厂商初始模板把 Swap 设成 0,或者根本没有启用交换分区。
- 业务明确有内存峰值需求,比如编译大型项目、运行内存型数据库,需要临时或者永久增加 Swap 空间。
- 服务器磁盘从机械盘换成了 SSD/NVMe,Swap 的性能特性和容量配比都需要重新估算。
这些场景下,如果每次都手动走一遍完整的调整流程,不仅耗时,还容易因为手误导致系统无法启动。把流程固化成脚本,是所有长期做服务器运维的人必然会走的一条路。
1.3 手动调 Swap 到底有多容易翻车
手动调整 Swap 的标准流程是:先确认当前 Swap 状态,执行swapoff -a停用所有交换分区,然后删除旧分区或者直接操作交换文件,再用mkswap格式化、swapon -a重新启用,最后还要同步修改/etc/fstab保证开机自动挂载。
听上去不难,但实际操作有非常多的细节坑。比如swapoff时系统物理内存不足,会导致操作卡住甚至 OOM;比如交换分区正在被大量使用时,直接强制卸载会有进程数据丢失风险;再比如修改/etc/fstab时写错 UUID,重启后系统直接进入维护模式。我见过不止一位同行因为这些细节在半夜爬起来排查问题。把这些操作全部封装进脚本,并加入必要的安全检查、日志输出、异常回滚,就能把风险控制在一个可控范围内。
2. 核心原理与方案选型拆解
2.1 Swap 调整的三要素:容量、挂载方式、持久化
一个合格的 Swap 调整脚本,必须同时处理好三件事:容量参数、Swap 的实现形态、开机自动挂载的持久化配置。
容量参数很好理解,就是你想把 Swap 调到多大。但问题是这个值不能乱填,它受物理内存大小限制,也受磁盘剩余空间限制。脚本在分配新 Swap 之前,必须先读取系统当前的内存总量和磁盘可用空间,做一次合法性校验,防止因为输入了过大的目标值导致磁盘写满。这个校验逻辑我放在脚本最前面,宁可报错退出,也不要带病执行。
Swap 的实现形态有两种:一种是传统的磁盘分区,用fdisk或gdisk划分独立分区;另一种是交换文件(swapfile),在现有文件系统中创建一个大文件,通过循环设备挂载成交换空间。我的脚本默认走交换文件路线。原因很明显:交换文件不需要提前规划磁盘分区,调整容量时直接删掉重建文件就行,特别适合云服务器和虚拟化环境。对于需要极致性能的裸金属高负载场景,独立的 Swap 分区可能稍好一点,但差异在 SSD 时代已经非常微小,对绝大多数业务完全可以忽略。
持久化方面,/etc/fstab需要新增一行类似swapfile swap swap defaults 0 0的配置,系统启动时才能自动挂载。但这里有一个非常隐蔽的问题:如果/etc/fstab里同时存在旧的 Swap 分区条目和新的 Swap 文件条目,swapon -a会尝试挂载两次,出现 UUID 冲突或文件冲突报错。脚本在写入新配置前,必须把旧的 Swap 相关行全部清理干净,这一步是整个持久化逻辑的关键。
2.2 调整流程的核心链路拆解
整个脚本的执行链路可以归纳为:环境检查 → 停用旧 Swap → 备份旧配置 → 创建新 Swap → 启用新 Swap → 更新开机配置 → 输出结果。
环境检查阶段要搞定几件事:确认当前用户是 root、确认swapoff、mkswap、fallocate、swapon这些核心工具是否可用、检查目标磁盘空间是否足够。这些检查看起来琐碎,但每一条都在帮你拦截同一个问题——脚本执行到一半突然失败。
停用旧 Swap 阶段,我要特别强调顺序问题。脚本会先查看/proc/swaps或者swapon --show的输出,确认当前有哪些交换设备在启用状态,然后逐个执行swapoff。这个阶段最怕的是内存不足。如果系统当前内存用量已经超过物理内存减去新 Swap 空间后的余量,swapoff会长时间卡住。我的处理方案是:在脚本里先用free -m获取可用内存,再用/proc/pressure/memory或者系统负载情况辅助判断,如果可用内存太低会给出警告,并默认不执行强制卸载。
备份旧配置主要针对/etc/fstab。脚本会先复制一份到/etc/fstab.bak.<时间戳>,后续不管出现什么问题,都能快速回滚。
创建新 Swap 阶段,我优先建议用fallocate创建交换文件。不过这里有个细节必须说明:某些文件系统(比如部分版本的 XFS)对fallocate分配的文件支持不太好,运行mkswap时可能报错。为了让脚本通用性更强,我加入了自动降级逻辑——fallocate失败就改用dd写入零字节。dd方式虽然创建大文件时耗时较长,但兼容性最稳。
启用和更新配置阶段,脚本先用mkswap格式化新文件,再用swapon启用,之后通过 sed 清理/etc/fstab中旧 Swap 行,再追加新配置。这里有一个我特意保留的设计:启用新 Swap 的操作在修改/etc/fstab之前完成。原因是如果/etc/fstab改错了,至少当前系统还可以正常工作,你不会被困在一个连交换空间都没有的状态里,回旋余地更大。
2.4 为什么脚本比 Ansible/Puppet 更合适
有同行可能会问:现在配置管理工具这么成熟,为什么不直接写 Ansible playbook,还要一个 Shell 脚本?
我的答案很简单:运维场景里有些问题不值得引入一套完整的配置管理全家桶。Swap 调整本质上是一个低频、独立、有明确边界的操作。公司可能只有三五台服务器,为了一个 Swap 调整任务专门维护一套 inventory 高可用设置,反而增加了理解成本和维护成本。Shell 脚本本身就是 Linux 的“母语”,一次移植到处跑,不需要依赖 Python 环境,不需要担心 Control Node 的连通问题,直接扔到任意一台机器上就能执行。
再者,这种脚本非常适合作为云服务器初始化流程的一部分。新机器交给业务前,顺手bash swap-setup.sh --size 8G一跑,连上 Swap 就位了,不需要拉一套 CMDB 系统过来做一次性操作。我自己的使用习惯就是把这脚本放进内部的基础镜像构建流程里,每次生成新模板自动调用一次,比配置管理工具更轻量、更直观。
3. 脚本落地:完整实现与逐段解析
3.1 实际运行效果预览
先说脚本用起来什么样,方便你对后面的代码有个整体的感知。假设我拿到一台新服务器,物理内存 16G,磁盘空闲空间充足,我打算把 Swap 设置为 4G。执行:
sudo bash swap-manager.sh --size 4G脚本输出大致是:
[Info] 当前用户: root,权限校验通过 [Info] 物理内存: 16384 MB [Info] Swap 现状: 2.0G 已启用 (/swap.img) [Info] 磁盘可用空间检查通过: 可分配 4G Swap 文件 [Info] 停用旧 Swap 设备... [Info] 已停用: /swap.img [Info] 创建新交换文件: /swap.img (大小: 4G) [Info] 使用 fallocate 创建成功 [Info] 已启用新的 Swap 文件 [Info] 更新 /etc/fstab 持久化配置 [Info] 调整完成,当前 Swap 状态: NAME TYPE SIZE USED PRIO /swap.img file 4G 0B -2整个从执行到结束不到 10 秒(创建文件时间取决于磁盘速度),期间每一步都有明确的日志输出,出现错误会直接打印[Error]并停止后续动作。
3.2 完整脚本代码
我把脚本完整贴出来,下面每个小节都会拆关键部分详细讲:
#!/usr/bin/env bash # # swap-manager.sh - Linux Swap 大小一键调整脚本 # 用法: # sudo bash swap-manager.sh --size 4G # sudo bash swap-manager.sh --size 4096M # sudo bash swap-manager.sh --size 4G --file /swap.img # set -euo pipefail # ---------- 全局配置 ---------- SWAP_FILE="${SWAP_FILE:-/swap.img}" VERBOSE=1 CURRENT_SWAP_SUM_MB=0 # ---------- 工具函数 ---------- log_info() { if [[ "${VERBOSE}" -eq 1 ]]; then echo -e "\033[32m[Info]\033[0m $*" fi } log_warn() { echo -e "\033[33m[Warn]\033[0m $*" } log_error() { echo -e "\033[31m[Error]\033[0m $*" exit 1 } # ---------- 参数解析 ---------- parse_args() { while [[ $# -gt 0 ]]; do case "$1" in --size) TARGET_SIZE_RAW="${2:-}" shift 2 ;; --file) SWAP_FILE="${2:-}" shift 2 ;; --quiet) VERBOSE=0 shift ;; --help|-h) echo "用法: $0 --size [大小] [--file 交换文件路径] [--quiet]" echo "示例: $0 --size 4G" echo " $0 --size 4096M --file /swapfile" exit 0 ;; *) log_error "未知参数: $1" ;; esac done } # 将 4G / 4096M / 8192K 统一转换为 MB 数字,方便后续计算 parse_size_to_mb() { local raw="$1" local num unit num=$(echo "$raw" | grep -oE '^[0-9]+') unit=$(echo "$raw" | grep -oE '[GgMmKk]?$') case "$unit" in G|g) echo $(( num * 1024 ));; M|m) echo "$num";; K|k) echo $(( num / 1024 ));; '') echo "$num";; *) log_error "无法识别的大小单位: $raw" ;; esac } check_environment() { if [[ $EUID -ne 0 ]]; then log_error "请使用 root 权限运行本脚本 (sudo)" fi for cmd in swapoff swapon mkswap fallocate dd free sed grep awk; do if ! command -v "$cmd" >/dev/null 2>&1; then log_error "缺少必要命令: $cmd" fi done if ! swapon --show >/dev/null 2>&1; then log_warn "当前没有已启用的 Swap 设备,将以新增模式执行" fi } get_current_swap_mb() { local total=0 while read -r name type size used prio; do if [[ "$name" == "NAME" ]]; then continue fi total=$(( total + size )) done < <(swapon --show --bytes 2>/dev/null | awk 'NR>1 {print $1, $2, $3, $4, $5}') echo $(( total / 1024 / 1024 )) # 字节转 MB } get_mem_mb() { free -m | awk '/^Mem:/ {print $2}' } get_disk_free_mb() { local dir dir=$(dirname "$SWAP_FILE") df -m "$dir" | awk 'NR==2 {print $4}' } # ---------- 主流程 ---------- stop_old_swap() { log_info "停用旧 Swap 设备..." local swap_names swap_names=$(swapon --show --noheadings --bytes | awk '{print $1}') if [[ -z "$swap_names" ]]; then log_info "没有需要停用的 Swap 设备" return 0 fi local mem_mb mem_mb=$(get_mem_mb) local used_mb used_mb=$(free -m | awk '/^Mem:/ {print $3}') if [[ $(( used_mb + 1024 )) -gt mem_mb ]]; then log_warn "内存余量不足,swapoff 可能卡住。当前已用 ${used_mb} MB / 共 ${mem_mb} MB" log_warn "建议先释放部分内存再执行,脚本尝试继续..." fi while read -r name; do if [[ -n "$name" ]]; then swapoff "$name" || log_error "停用 ${name} 失败" log_info "已停用: $name" fi done < <(echo "$swap_names") } create_swap_file() { local target_mb="$1" local dir dir=$(dirname "$SWAP_FILE") if [[ -f "$SWAP_FILE" ]]; then rm -f "$SWAP_FILE" log_info "已清理旧的交换文件: $SWAP_FILE" fi local disk_free disk_free=$(get_disk_free_mb) if [[ "$disk_free" -lt $(( target_mb + 512 )) ]]; then log_error "磁盘剩余空间不足,需要至少 $(( target_mb + 512 )) MB,实际剩余 ${disk_free} MB" fi log_info "创建新交换文件: $SWAP_FILE (大小: ${target_mb} MB)" if ! fallocate -l "${target_mb}M" "$SWAP_FILE" 2>/dev/null; then log_warn "fallocate 方式失败,切换到 dd 写零模式" dd if=/dev/zero of="$SWAP_FILE" bs=1M count="$target_mb" status=progress fi chmod 600 "$SWAP_FILE" mkswap "$SWAP_FILE" || log_error "mkswap 格式化失败" } enable_new_swap() { log_info "启用新的 Swap 文件..." swapon "$SWAP_FILE" || log_error "swapon 启用失败" } update_fstab() { log_info "更新 /etc/fstab 持久化配置..." local ts ts=$(date +%Y%m%d%H%M%S) cp /etc/fstab "/etc/fstab.bak.${ts}" # 清理所有旧 swap 相关行 sed -i '/swap/d' /etc/fstab # 追加新配置 echo "${SWAP_FILE} none swap sw 0 0" >> /etc/fstab log_info "已写入 /etc/fstab,备份位于 /etc/fstab.bak.${ts}" log_info "当前 /etc/fstab 中的 Swap 配置:" grep -E 'swap' /etc/fstab } # ---------- 执行入口 ---------- main() { parse_args "$@" if [[ -z "${TARGET_SIZE_RAW:-}" ]]; then log_error "必须提供 --size 参数" fi local target_mb target_mb=$(parse_size_to_mb "$TARGET_SIZE_RAW") check_environment local mem_mb mem_mb=$(get_mem_mb) local disk_free disk_free=$(get_disk_free_mb) log_info "物理内存: ${mem_mb} MB" log_info "磁盘可用空间: ${disk_free} MB" if [[ "$target_mb" -gt "$mem_mb" ]]; then log_warn "目标 Swap 大于物理内存,指标本身存在风险,继续执行" fi stop_old_swap create_swap_file "$target_mb" enable_new_swap update_fstab log_info "调整完成,当前 Swap 状态:" swapon --show } main "$@"3.3 关键函数逐段拆解
参数解析与大小转换:脚本支持--size 4G、--size 4096M、--size 8192K三种写法,内部统一换算成 MB。为什么不用字节做单位?因为 shell 做整数运算时,字节数动辄几十亿,计算容易溢出,而且free -m、df -m这些系统工具原生输出也是 MB,统一单位方便对比和校验。这个parse_size_to_mb函数相当巧妙,用正则先把数字和单位分开,再按单位乘除,万一写了个"abc"进来,正则匹配不到数字会直接给 0,虽然不会有语法错误,但后续逻辑会把它当成非法参数处理。这个函数还有一个隐藏细节:它把G和g、M和m都做了大小写兼容,原因是有同行会把参数写成-size 4g。
环境检查函数:脚本一上来就用set -euo pipefail,这行配置是 Shell 脚本的“安全气囊”。-e确保任何一条命令失败就立即退出,-u禁用未定义变量,pipefail更严格,它保证管道中只要有一环失败,整个管道的退出状态就是失败。这三个选项配合起来,能避免很多“脚本执行了一部分,但错误被悄悄吞掉”的默认状态。EUID检查目的是要求 root 权限,因为修改/etc/fstab、执行mkswap都需要 root,非 root 用户就算侥幸跑完也没有实用价值,不如一开始就拦住。
swap 停用函数:stop_old_swap被设计成先列出所有已启用的 Swap 设备,再逐个停用。这里我特别处理了一个容易翻车的点:在swapoff前检查内存余量。逻辑是used_mb + 1024 > mem_mb则提示内存余量不足,因为swapoff需要把 Swap 中的数据全部搬回物理内存,如果物理内存余量不够,内核会反复尝试搬移,表现为系统响应变慢,极端情况下触发 OOM。这个检查只是告警,不硬阻断,因为有些场景下 Swap 里其实没多少活跃数据,直接swapoff也能瞬间完成,拦截太死反而让人无法处理特殊情况。
创建 Swap 文件:create_swap_file的第一步是删除旧的交换文件。这里必须加[[ -f "$SWAP_FILE" ]]判断,否则文件不存在直接rm -f也没问题。接下来是磁盘空间预检:用df -m获取目标目录剩余空间,目标大小加上 512MB 的余量作为硬性门槛。这个 512MB 的余量是从我几次翻车经验里总结出来的——文件系统本身有元数据开销,而且 Swap 文件创建后 mkswap 会写入少量格式化数据,如果磁盘刚好只剩目标大小的空间,最后一步往往会在mkswap时报错说设备空间不足。预检通过后才真正进入创建环节。
fallocate vs dd:fallocate是 POSIX 标准里推荐的快速分配文件方式,它利用文件系统的extent机制直接占用磁盘块,不需要写数据,因此大型文件可以秒建。但它的硬伤在于某些文件系统对稀疏文件的处理方式特殊,mkswap 会拒绝使用这样的文件。我的脚本捕获fallocate失败后自动回退到dd if=/dev/zero。dd方式是逐块写入真实数据,慢但稳,在 XFS、Btrfs、ext3/4 上都能正常工作。要注意的是status=progress参数是 GNU coreutils 8.24 之后的版本才支持,老版本会报错,如果你的服务器内核比较老,建议把这个参数去掉。
fstab 更新函数:update_fstab是整个脚本里最需要谨慎的部分。我先备份,再执行sed -i '/swap/d' /etc/fstab清理所有含swap字符串的行,最后追加新配置。这里有一个比较隐蔽的问题:sed -i '/swap/d'会把所有包含关键字swap的行都删掉,比如注释行# 这是我配置的swap,或者类似swappiness的配置行也会被误删。在绝大多数常规 fstab 文件中不会出现这些古怪行,但为了稳妥,脚本的sed命令还可以优化成更精确的匹配模式,比如/swap\s/或者/swapfile|swap.img/。我在写博文版本的脚本里保留了一个简化的模式,你实际部署时可以根据自己服务器 fstab 的情况改这一行,避免误伤其他配置。
4. 实操过程记录与参数讨论
4.1 一次真实的调整过程:从 2G 到 8G
我在一台 16G 内存的测试服务器上完整跑过一次调整流程,机器原本的 Swap 是 2G 交换文件,位于/swap.img。
执行前先用free -h看内存状态,再用swapon --show看交换空间现状,这是每个运维在执行任何 Swap 操作前都该做的第一步。当前系统已用内存 11G,有充足余量,说明swapoff不会卡顿。
脚本执行后,先打印物理内存 16G、磁盘可用空间 82G,接着自动停用旧 Swap。swapoff /swap.img的执行过程大约耗时 1 秒,说明旧 Swap 中实际驻留的数据很少。随后脚本删除旧文件,用fallocate -l 8192M /swap.img秒建新文件,紧接着chmod 600和mkswap格式化。格式化完成后swapon /swap.img启用,最后脚本自动修改/etc/fstab。
执行完我特意重启了一次服务器验证持久化是否生效。重启后free -h显示 Swap 8G,swapon --show输出正常,说明/etc/fstab里的配置完全正确。整个过程中唯一让人紧张的是sed -i '/swap/d'清理旧行的那一瞬间,重启前我反复检查过/etc/fstab的内容,确认只有一条/swap.img none swap sw 0 0,没有多余的行残留。
4.2 Swap 大小的常规经验值
我整理了这些年常用的 Swap 分配经验值,可以直接对照参考:
| 物理内存 | 推荐 Swap 大小 | 适用场景说明 |
|---|---|---|
| 2G 以下 | 物理内存的 2 倍 | 小内存服务器,Swap 主要用于缓解内存不足 |
| 2G - 8G | 物理内存的 1 倍 | 常规 Web 服务、开发环境 |
| 8G - 32G | 4G - 16G 之间 | 数据库、中间件等内存消耗稳定的服务 |
| 32G 以上 | 8G - 16G | 大型应用服务器,Swap 更多是兜底而非主力 |
| 任意内存 | 0(完全禁用) | 不建议,除非业务对延迟极敏感且内存冗余充足 |
这套经验值不是绝对的,真正决定 Swap 大小的其实是你业务的“内存峰值”。比如 Java 应用堆内存设了 4G,元空间、线程栈、堆外内存还额外消耗 2G,机器总内存 16G,业务高峰期内存用量可能冲到 13G 以上,那 Swap 给 2G 可能就不太够。我觉得最稳妥的方法是:先按经验值给一个初始分配,然后配合监控工具跑一周,观察vmstat里si和so(swap in / swap out)两个指标,如果si频繁不为 0,说明物理内存确实不够,Swap 在持续兜底,就该扩容或者加内存了。
4.3 实操时必须注意的命令参数细节
mkswap之前的权限与所有权:交换文件建好后一定要chmod 600,否则swapon会提示 “insecure permissions”,拒绝启用。这是 Linux 的安全机制,因为 Swap 文件内容可能包含内存中的敏感数据,必须有严格的权限控制。
swapon --show与/proc/swaps的关系:swapon --show本质是读取/proc/swaps再排版显示,两者数据一致。脚本里我统一用swapon --show,因为输出更清晰,还能加--bytes参数让输出的字节数不带单位,便于脚本内再次计算。写过 Shell 脚本的都知道,从命令输出里解析带G、M后缀的字段简直是自找麻烦,所以能用纯数字输出一定用纯数字。
内存单位为 MB 而非 KB,避免整数溢出:脚本内多次用free -m,直接用free -k可能出现 32 位整数上限问题。现代服务器内存动辄 64G,KB 单位的数字轻松超过 6 千万,虽然 shell 的整数计算在 64 位系统上通常没问题,但在 32 位系统上就可能溢出。使用 MB 单位减小了数值范围,计算更安全。这个细节如果你写其他系统管理脚本时也会遇到,值得记住。
4.4 高频故障速查表
我在实际帮助别人排查 Swap 问题时,整理过一张问题速查表,这里直接分享:
| 现象 | 可能原因 | 快速处理办法 |
|---|---|---|
swapoff长时间无响应 | 内存余量不足,或 Swap 中活跃数据过多被反复搬移 | 别硬等,观察free -m和vmstat,必要时先释放缓存:sync; echo 3 > /proc/sys/vm/drop_caches |
swapon报权限不安全 | Swap 文件权限过于宽松 | 执行chmod 600 /swap.img后再试 |
| 重启后 Swap 不生效 | /etc/fstab未写入或写法有误 | 检查 fstab,确认路径、挂载点、文件系统类型三列正确 |
| 创建大 Swap 文件时磁盘直接满 | 未做剩余空间校验 | 用df -h提前确认,脚本已内置 512MB 余量保护 |
mkswap提示设备文件过小 | fallocate建的文件可能不被部分文件系统识别 | 改用dd if=/dev/zero of=/swap.img bs=1M count=大小 |
| 系统启动卡住 | fstab 有误导致挂载等待超时 | 进入单用户模式,修正或注释异常行 |
5. 脚本工程化与后续扩展
5.1 支持交互式执行,弥补参数遗漏
目前的脚本全部依赖命令行参数传入。但现实中总有人忘记写--size,或者拿到脚本后第一反应是“我该填多大?”。我在迭代版本里加了一个交互模式:参数为空时,系统主动读取当前物理内存,弹出提示“当前物理内存 16G,建议分配 4G 或 8G,请输入目标大小:”,用户直接输入4G回车即可。这比直接报错友好得多。
交互模式的实现逻辑很简单,主函数里加一个分支:
if [[ -z "${TARGET_SIZE_RAW:-}" ]]; then local mem_default mem_default=$(get_mem_mb) local suggestions if [[ "$mem_default" -ge 16384 ]]; then suggestions="8G" else suggestions="4G" fi read -r -p "请输入目标 Swap 大小(例如 ${suggestions}): " TARGET_SIZE_RAW if [[ -z "$TARGET_SIZE_RAW" ]]; then TARGET_SIZE_RAW="$suggestions" fi fi这个小功能特别适合新同事使用。我曾经看到有人手动把 Swap 调成 64G,理由是“磁盘反正有空间,Swap 越大越好”,结果业务内存很少用到 Swap 区,白白占用磁盘读写资源。有了建议值交互,至少能保证走一遍正常思考路径。
5.2 兼容 systemd 与 SysV init 环境的差异
现代 Linux 发行版基本都用 systemd,但老企业里仍然存在 CentOS 6 这种 SysV init 环境。systemd 里如果fstab写错了导致启动失败,会有较长的超时时间,而且日志不太好定位;SysV init 环境则相对脆弱,fstab 错误可能直接导致无法进入系统。为了让脚本在两个环境下都稳妥,我在update_fstab里不仅备份了文件,还增加了写前验证步骤:写入新配置前,先用findmnt -v --noheadings --target /检查根文件系统是否正常,倘若异常直接中止写入并提示恢复备份。
另外,systemd 环境下最好禁用旧的 swap 单元:
systemctl mask 'dev-swap.img.swap' 2>/dev/null || true这样做可以防止 systemd 启动时既走/etc/fstab又走单元文件,造成双重挂载。云厂商默认镜像里的/swap.img条目有时会和系统的默认 unit 冲突,如果你的环境中出现了这类情况,上面这条命令是必要的补充。
5.3 日志与回滚机制的设计思路
我的脚本目前只输出到终端,但生产环境里建议加上日志落盘。最简单的方式是在脚本开头重定向一份日志:
exec > >(tee -a /var/log/swap-manager.log) 2>&1这样做有个好处,问题发生后你能看到完整的执行时间线,而不是听同事描述“好像跑了,好像失败了”。回滚机制上,脚本已经在每次修改/etc/fstab前自动备份,回滚命令也非常简单:
sudo cp /etc/fstab.bak.<时间戳> /etc/fstab sudo swapoff /swap.img把这两条命令再封装成一个--rollback参数,就能实现任意时间点的完整回退。我在实际使用中发现,回滚功能比预想中更常用。比如有一次我在老同事的服务器上执行调整时,发现他原来用的是一个独立的 Swap 分区/dev/sdb2,而我的脚本清理旧行时把它也删掉了。幸好有备份,一条cp命令就恢复了。这也提醒我:脚本默认只处理交换文件,如果机器原本用的是独立交换分区,执行前你得提前确认并调整脚本的清理规则。
5.4 几个云平台与虚拟化环境的使用建议
云服务器厂商的初始化镜像里,Swap 的默认配置各不相同,有的给 2G 交换文件,有的干脆不给,还有的会放在根分区的 LVM 逻辑卷里。遇到后面这种情况时,脚本里的SWAP_FILE路径就不能照抄默认值。建议用lsblk先看清楚磁盘布局,确认 Swap 是用独立分区还是文件形式,再决定要不要用我的脚本跑。
虚拟化集群里,我最想提醒的一点是:不要把 Swap 建在容器宿主机的高负载磁盘上。容器运行时的 IO 竞争已经很大,Swap 文件频繁读写会加剧延迟。如果你用的是 Ceph 这种分布式存储,Swap 文件的性能更加不可控,建议要么增大物理内存减少 Swap 需求,要么把 Swap 放到本地 NVMe 磁盘,思路比代码优先,这是脚本无法替你做的判断。
这台机器上还得完成三个最后步骤
脚本基本成型之后,我建议你再做三件小事,让它真正变成你的日常工具。
第一,把脚本放到一个统一管理目录,顺手加进 cron 做一次季度巡检,通过swapon --show和df -h检查 Swap 容量是否仍在预期区间。第二,把脚本纳入新机器初始化模板,每次装好系统自动跑一次,避免业务上线后才发现 Swap 配置不合理。第三,也是最重要的,真正在你自己的临时测试机上跑两遍,第一遍用--quiet观察命令行为,第二遍全程默认输出,确认 swapoff、mkswap、swapon 三个阶段都没有异常后再上生产机器。
我在实际运维中越来越深的体会是:Swap 调整本身不是难事,难的是把边界情况、故障预案、操作顺序都考虑在内之后,还能保持一套简单可靠的流程。脚本写得好不好,不是看代码多少,而是看它在意外面前能不能给出清晰的反馈,能不能让你在半夜被电话叫醒时不慌不忙地查日志。希望这版脚本能帮你省下这些不必要的麻烦。