【VPS 硬件选型指南 (内存 篇) 06】VPS 内存爆了怎么办:swap 是什么、burst RAM 与 OOM killer 自救

VPS 内存耗尽时,Linux 会先用 swap 救急,再用 OOM killer 杀进程。本文讲清 swap、swappiness、burst RAM 真相,并给出加 swap 文件、护数据库、WordPress 2G 舒适线的实操方法。

延伸阅读

更多相关攻略推荐:【VPS 硬件选型指南 (CPU 篇) 04】AMD EPYC 还是【发行版选型 07】Arch Linux 深度科普:滚动发布、pac【VPS 进阶玩法精选 014】ARM 架构 VPS 值不值得上?A【对象存储 01】对象存储怎么选:Backblaze B2 vs C【TCP优化 01】美国 VPS 跑不快?多半是没开 BBR,一个命

一、先别慌:内存爆了到底发生了什么

某天你 SSH 登录 VPS,发现网站打不开,或者 SSH 自己都被挤掉了。第一反应往往是"是不是被攻击了"或者"是不是系统坏了"。其实更常见的元凶很简单:内存(RAM)用光了。

Linux 处理内存不是一上来就崩,它有一套层层递进的缓冲机制。理解这套机制,比急着花钱升级套餐重要得多。

整个过程可以想象成一条流水线:第一步,程序申请的内存先放进物理内存(RAM),这是最快的地方;第二步,当 RAM 快要塞满,内核会把那些"暂时不翻"的内存页搬到磁盘上的一块专用空间,也就是 swap;第三步,如果 swap 也写满了,内核就不得不启动 OOM killer,按算法挑出占用内存最多的进程,发一个 SIGKILL 直接杀掉,腾出空间让系统活下来。

换句话说,OOM killer 其实是系统的"最后防线",它宁肯杀掉一个进程,也不愿让整台机器彻底卡死。问题只是,它挑中的往往正好是你最不想丢的,比如数据库。

需要特别注意:如果一台机器根本没有配置 swap,那么一部分叫"匿名内存页"的数据(比如程序运行时的堆内存)根本无处可去,系统会提前进入 OOM 状态,反而更脆弱。所以加 swap 的第一个意义,不是让你"内存变大",而是给你争取喘息时间。

二、swap 是什么:磁盘上的"溢出内存"

swap 本质上就是硬盘上划出来的一块专用区域,专门承接 RAM 溢出后的冷数据。你可以把它理解成办公桌旁边的一个储物柜:桌面(RAM)放正在用的文件,柜子(swap)放暂时不翻的档案。柜子能救急,但每次开柜取东西都比在桌面上拿慢得多。

慢多少?差的是数量级。RAM 的带宽以 GB/s 计,随机访问在纳秒级;而即便是 NVMe 固态硬盘,延迟也比内存高几千倍,机械盘的 IOPS 更是天差地别。所以 swap 绝不等于"免费内存",它只是在 RAM 见底时,用速度换生存。

swap 有两种形态:一种是单独的分区(swap partition),另一种是普通文件(swap file)。云厂商的 KVM VPS 通常会自带一小块 swap 分区,常见 256MB 左右;如果你没分到,或者分到的不够,完全可以自己建一个 swap 文件,我们在第三节会手把手做。

关于"要不要 swap"一直有争论。一种观点认为,服务器就该关掉 swap,强迫程序在纯 RAM 里运行,避免慢速换页拖垮性能。这个思路对某些极致追求延迟的场景成立,但对大多数中小站长,保留适量 swap 是更稳妥的选择,它能在突发流量或内存泄漏初期给你报警和处理的窗口,而不是瞬间崩盘。

另一个常见误解是"swap 用得多说明内存一定不够"。其实 Linux 默认很乐意把用不上的内存页提前换出去,哪怕 RAM 还有空余,这是正常的缓存策略,不代表危险。真正要看的是 swap 是不是被持续写满,以及是不是伴随着大量的磁盘 I/O 等待。

三、实战:3 分钟给 VPS 加一个 swap 文件

下面这套流程在 Ubuntu 22.04、24.04 和 Debian 12 上实测可用,默认你用的是 root 或能 sudo 的账户。核心思路是:先确认没有 swap,再用 fallocate 建文件,设权限,格式化,启用,最后写进 /etc/fstab 让开机自动挂载。

第一步,先看机器现在有没有 swap。运行 sudo swapon --show,如果什么都没输出,说明没有启用 swap;再用 free -h 看一眼内存和 swap 总量。

第二步,建一个 2G 的 swap 文件。命令是 sudo fallocate -l 2G /swapfile。fallocate 的优点是瞬间分配,不真的往磁盘写数据。如果你的文件系统是 Btrfs 或者 NFS,fallocate 可能建出一个"稀疏文件",导致 swap 行为异常,这时就要改用 dd if=/dev/zero of=/swapfile bs=1M count=2048,老老实实把 2G 的 0 写满,速度慢些但稳。

第三步,设权限。swap 文件里可能残留敏感内存内容,必须只让 root 访问:sudo chmod 600 /swapfile。如果权限太开放,系统甚至可能拒绝启用它。

第四步,格式化为 swap:sudo mkswap /swapfile。

第五步,立即启用:sudo swapon /swapfile,然后再次 sudo swapon --show 验证是否出现 /swapfile。

第六步,写入 /etc/fstab 实现永久挂载。追加一行 /swapfile none swap sw 0 0 即可。这样下次重启后 swap 自动生效,不用每次手动开。

swap 文件多大合适?常见区间是 1G 到 4G。如果你的真实内存(Guaranteed RAM)只有 1G,加 2G swap 能明显缓解 OOM;如果本身就有 2G 以上 RAM,1G swap 作为缓冲通常就够了。注意 swap 不是越大越好,太大只会让机器在"慢速换页"里越陷越深,反而掩盖了真正的内存不足。

四、swappiness:让内核别太勤快地把内存换出去

加好 swap 之后,还有一个关键旋钮叫 swappiness,它控制内核把内存页换到磁盘的"激进程度"。取值从 0 到 100:值越低,内核越不愿意换页,倾向于把数据留在 RAM;值越高,内核越早把冷数据搬去 swap。

查看当前值:cat /proc/sys/vm/swappiness。临时修改:sudo sysctl vm.swappiness=10。想永久生效,就在 /etc/sysctl.conf 里加一行 vm.swappiness=10,重启后依旧保留。

这里有个容易踩坑的地方:关于默认值,不同资料说法不一。Ubuntu 桌面版默认是 60,倾向上把内存换出去以释放 RAM;但服务器场景完全不同,Ubuntu Server 以及不少云厂商建议服务器用 10,DigitalOcean 也明确建议服务器设 10。

为什么服务器要设 10?因为数据库服务器(比如 MySQL)高度依赖内存里的缓冲池,如果把数据库页频繁换到磁盘,查询延迟会肉眼可见地变差。把 swappiness 压到 10,等于告诉内核"除非真没辙了,别动我的内存页",这对 Web 和数据库服务最友好。

另一个相关的旋钮是 vm.vfs_cache_pressure,默认 100,它控制文件系统元数据缓存的回收倾向。一般情况下保持默认即可,新手不必乱动。

总结一个实用的经验表:纯 Web 服务器、数据库服务器、以及编译或构建机器,swappiness 都建议 10;桌面或交互式环境才用默认的 60 来换取响应速度。你的 VPS 既然是服务器,无脑设 10 基本不会错。

五、burst RAM 的真相:为什么"突发内存"是个美丽的谎言

买 VPS 时你一定见过这样的套餐:"512MB 保证内存 + 1GB 突发内存"。听起来很香,实际上这是 OpenVZ 虚拟化时代的老话术,今天的行业共识是:Burst RAM is a lie,突发内存是个谎言。

要拆穿它,得先分清两个概念。Guaranteed RAM(保证内存)是真正随时属于你的,邻居抢不走;Burstable RAM(突发内存)则是邻居不用时你才能借的"闲内存",一旦邻居要把内存要回去,你必须在瞬间释放掉那块借来的空间。

麻烦在哪?如果那一刻 MySQL 正好用着这块借来的内存,你来不及优雅释放,OOM killer 就会直接杀掉你的数据库,网站当场崩溃。所以突发内存不是"额外送你的内存",而是一颗随时可能引爆的雷。

较新型的 OpenVZ(2.6.32 内核那代)引入了 vSwap,比老式 burst 稳一点,至少不会因为邻居收回内存而直接丢数据,但它依然不是保证可用。对比之下,KVM、Xen 这类主流虚拟化给你的通常是真实 swap(KVM 常自带 256MB 左右 swap),比 OpenVZ 的"假突发"靠谱得多。

给新手一个最实在的建议:规划内存时,永远只按 Guaranteed RAM 算,把突发内存当成"运气好才有、千万别依赖"的赠品。生产环境尤其如此。

打个比方,突发内存就像合租公寓里"邻居不在时你才能用的客厅"。标称你能用 1G,但那是邻居出门时借的;邻居一回家你就得立刻腾地方,正用着的东西被强行收走,自然就乱套崩了。所以你只该按自己卧室(Guaranteed RAM)的大小来安排家具。

六、OOM killer:内核在深夜杀掉你的 MySQL 时留下了什么

当内存真的耗尽,OOM killer 登场。它怎么选"受害者"?内核给每个进程算一个 oom_score,分数越高越先死。真正的旋钮是 oom_score_adj,范围从 -1000 到 +1000:写 -1000 几乎等于"免死金牌",写 +1000 则是"优先牺牲"。

想保护某个关键进程,比如 MySQL,可以执行 echo -1000 > /proc/$PID/oom_score_adj,把 $PID 换成 mysqld 的进程号。如果是 systemd 管理的服务,更规范的做法是在单元文件里加一行 OOMScoreAdjust=-1000,重启后即永久生效。

进程被杀之后,内核一定会留下日志,这是事后破案的关键。查法有两条:dmesg | grep -i "out of memory",或者 dmesg | grep -i "killed process";用 systemd 的机器也可以 journalctl -k | grep -i oom。日志里通常长这样:Out of memory: Killed process 12345 (mysqld) total-vm:1234567kB, anon-rss:123456kB, file-rss:1234kB。看到 mysqld 被 Killed,就知道是内存爆了导致的数据库消失。

还有一个好消息:Linux 4.6 之后的内核多了 OOM reaper 这个内核线程。以前被杀的进程可能卡在不可中断的 I/O 里,内存迟迟不释放,整台机器还是卡;现在 reaper 能在进程"濒死"时就立刻回收它的匿名内存,恢复速度快很多。

一个真实的案例:某站长早上发现数据库不见了,网站打不开。翻 dmesg 看到一句 "Killed process 12345 (mysqld)",才意识到是半夜内存爆了被 OOM 干掉。解法组合是:给 MySQL 设 OOMScoreAdjust=-1000、限制 innodb_buffer_pool_size 别吃太多、整机再加 2G swap。三管齐下后再没复发。

七、WordPress 为什么爱爆内存:2G 才是舒适线

WordPress 是 VPS 内存杀手榜的常客。官方最低要求写得很理想(PHP 7.3 以上、MySQL 5.6 以上、256MB 内存),但那是理论下限。生产环境的真实结论是:1GB 是"绝对下限但不推荐",2GB RAM 才是 WordPress(PHP 8.x 配 MySQL/MariaDB 配 Nginx 或 Apache)稳定运行的现实舒适线。

为什么 1G 一开就崩?因为内存的去向比你想象的分散。PHP-FPM 加 Nginx/Apache 常驻就要吃掉约 300 到 500MB;MySQL 的 InnoDB 缓冲池 innodb_buffer_pool_size 建议占 RAM 的 50% 到 75%(4GB 机器建议 2 到 3GB);再加上缓存插件、自动更新、安全插件一开,1G 机瞬间吃满,OOM killer 就来敲门了。

常见的调优数字:PHP 的 memory_limit 默认 128M,建议设 256M;如果跑的是 WooCommerce 这类重电商插件,推荐 512M 以上。注意,就算你的主机有 4G 内存,这个值不调照样可能报错,因为它限制的是单个 PHP 进程的上限。

给一张按场景的内存参考表:纯学习或极简静态站,1GB 是绝对下限,得没缓存、少插件才稳;WordPress 个人博客,2GB 是现实舒适线,swappiness 设 10;多插件或 WooCommerce,4GB 起步,php memory_limit 设 512M;日访问几千的大型站点,8GB 以上,还需 Nginx 加 PHP-FPM 调优加对象缓存;至于桌面或交互环境,swappiness 才用默认的 60。

新手常见翻车:买 1G VPS 装 WordPress,开缓存插件加自动更新后内存吃满 OOM。升级到 2G 或加 2G swap 后往往立刻稳定。所以如果你正纠结买多大,默认往 2G 想,大概率不会错。

八、给服务戴上"内存手铐":systemd 限内存 + MySQL 缓冲池

前面讲的都是"内存不够时怎么办"。更进阶的思路是"防患于未然":给每个服务单独戴上内存上限,防止某一个贪吃进程把整台机器拖垮。

systemd 原生支持 cgroup 内存限制。编辑服务的单元文件,在 [Service] 段加入 MemoryMax=1G(硬性上限,超了就被 OOM 杀)和 MemoryHigh=800M(软上限,超过就触发剧烈回收压力,相当于节流)。同样可以加 OOMScoreAdjust=-500 调低被杀优先级。改完执行 systemctl daemon-reload 再 restart 该服务即可。想临时改也行:systemctl set-property nginx.service MemoryMax=4G。验证就 cat 一下 /sys/fs/cgroup/system.slice/对应服务/memory.max。

对数据库,除了限内存,更关键的是调好 innodb_buffer_pool_size。在 1G 机上别贪心,把它控制在 512M 左右;2G 机可以设到 1 到 1.5G;4G 机设 2 到 3G。这个值是 MySQL 性能的灵魂,设小了查询慢,设大了抢光系统内存招来 OOM。

把三件事一起做:整机留 2G swap 当缓冲,给 MySQL 设 OOMScoreAdjust=-1000,再按机器大小配好 innodb_buffer_pool_size,最后用 systemd 给 Nginx、PHP-FPM 各戴个 MemoryMax 手铐。这样即便某个插件抽风吃内存,也不至于连数据库一起带走。

#VPS内存 #swap #OOMkiller #WordPress优化

💡 延伸阅读:关于架构与基础设施的更多深潜指南,请前往 VPS 主题导读中心 (Hub) 获取全盘策略。