ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

2G内存服务器榨干记:Swap虚拟内存一年省下几百块

2G内存服务器榨干记:Swap虚拟内存一年省下几百块 1. 2G内存的服务器是怎么被榨干的——先说清楚内存去哪了很多朋友买服务器预算卡得死死的2G内存的小鸡一年下来确实能省不少我手头这台就是这种配置。一开始装了个轻量博客加一个MySQL数据库跑起来还挺欢但时间一长东西一多问题就来了。内存这玩意不会凭空消失你想知道为什么不够用就得先把每一块用到哪里去盘清楚。1.1 2G内存真实分布系统、网站、数据库2G内存看着不多但拆开算笔账就明白它到底紧张在哪。我这里以一台典型的Ubuntu 22.04 LTS服务器为例装完基础系统之后内核、systemd那一堆服务、SSH、cron这些常驻进程大概会吃掉150到250MB内存。你没看错光是一个干干净净的系统内存已经少了十分之一。接下来是Web服务。如果你用的是Nginx加PHP-FPM的组合那内存消耗的大头就来了。每个PHP-FPM进程在默认配置下通常会占用30到60MB内存具体看跑的是什么程序。我这边跑的是一个带后台管理的动态站高峰期需要七八个PHP进程同时在线这一下子就是300到500MB。Nginx本身吃不了多少几十MB搞定但如果开启了访问日志、代理缓存之类再追加一部分。真正让我内存告急的其实是数据库。MySQL或者MariaDB启动之后会给自己分配一堆内存池innodb_buffer_pool_size默认情况下在2G内存的机器上可能会占到256到512MB再加各种连接缓存、临时表随便一跑就是500MB起步。如果你在上面再装个Redis虽然小又得占个几十MB但多个几十MB对于2G内存来说已经太多。这三块加起来我算过一笔账系统200MBPHP-FPM 400MBMySQL 600MBRedis 80MB再加点杂七杂八的进程和日志缓冲还没开始干活内存占用已经超过1.3G。剩下不到700MB看着好像还有富余但你打开网页、执行后台任务、跑定期脚本的时候内存需求会瞬间往上跳。我记得最夸张的一次系统负载直接飙到5以上SSH连上去敲个命令都要等好几秒那就是内存不够用的前兆。1.2 内存不足不是立刻崩溃而是先变卡后被杀进程2G内存不够用的时候不是一个炸弹“砰”的一下子炸掉它会先让你觉得服务器越来越迟钝。Linux的内存管理机制有一个很重要的概念叫OOM Killer也就是内存耗尽时的最终裁决者。它会在系统物理内存和Swap都用光之后挑一个“看起来最占内存又不是那么重要”的进程直接杀掉以保系统核心能继续跑。我第一次遇到这种情况是在某个深夜跑数据导入脚本。MySQL一大早就把内存吃得差不多了到了夜里脚本一跑内存直接见底。OOM Killer二话不说把正在执行脚本的进程杀了紧接着我的网站就开始报数据库连接错误手机上收到一大堆告警通知。后来我上去查日志看到了Out of memory: Kill process这样的记录才知道刚才发生了什么。比被杀进程更讨厌的是“假死”状态。内存不足会导致系统疯狂做换页操作也就是把内存里的数据往磁盘上倒腾磁盘速度比内存慢几个数量级服务器就会变得极其卡顿。SSH连上去能用但每次命令执行都要等网页打开要等好几秒甚至超时这种体验比直接挂了还折磨人。所以当务之急要么花钱升级内存要么想办法让现有内存储能。我当时手里的预算很紧一年也就几百块钱的服务器费用实在不想因为内存再掏钱于是把目光放在了SWAP虚拟内存上。2. 为什么我选择SWAP而不是直接升配——成本账其实很好算我不是一开始就想着用SWAP的毕竟这玩意儿在前几年风评并不好很多人都说Swap就是“卡”的代名词。但卡不卡得看你用在哪怎么配置还得看你的钱包允不允许。2.1 升级服务器的账单先算一下升配成本。我用的这台2G内存服务器年付大概在300到400元上下。如果升级到4G内存大多数云厂商的差价每月在30到60元之间一年下来就是360到720元。有的厂商更狠内存翻倍得跟着升套餐磁盘和带宽也被迫一起升级那成本就不是翻一倍的问题了分分钟要上千。如果只是临时内存不够比如我要跑一个为期三个月的数据抓取和分析任务那升级一年的成本就太亏了。任务跑完4G内存又用不上续费还得多花钱性价比极低。这时候用SWAP顶上一年下来一分钱不用多花顶多多占用一点磁盘空间这就是我标题里说的一年省下几百块的来源。但是得说清楚SWAP不是万能药。如果你跑的是内存密集型应用比如Java写的服务、复杂的机器学习推理、大规模数据库那SWAP可能根本救不了你甚至会让情况更糟。因为SWAP本质上是拿磁盘空间当内存用磁盘的读写速度只有内存的几十分之一一旦程序频繁访问Swap上的数据整个系统的响应速度会直线下降比你想象的还要明显。2.2 什么场景适合用SWAP救急什么场景不能硬撑我总结了一下适合用SWAP的有这么几种一是网站访问有明显波峰波谷平时内存用不了多少偶尔流量进来内存瞬间吃紧二是后台跑定时脚本比如每天凌晨执行一次备份、日志切割这种任务内存需求大但持续时间短三是多开几个轻量进程内存总量不够但每个进程都不算大偶尔有个别进程卡住也不至于全盘崩溃。不适合用SWAP的也很多比如数据库作为主要业务的服务器。数据库的随机读写需要极快的内存响应如果频繁命中Swap磁盘I/O会变成最大瓶颈查询延迟会从几毫秒涨到几百毫秒甚至秒级用户直接就会感知到“这网站怎么这么慢”。再比如Java或者Go写的高并发服务它们在JVM和自己运行时对内存有严格的管理策略SWAP的介入反而会干扰GC的判断导致频繁的垃圾回收停顿。所以我的逻辑很简单先判断自己的业务类型再决定要不要上SWAP。我这边以动态网站加数据库为主属于中轻度负载SWAP完全可以作为一个缓冲层把“内存不够导致进程被杀死”的问题变成“偶尔用一下Swap缓解尖峰压力”这是很划算的。3. SWAP虚拟内存的核心原理内存不够时谁该去磁盘上待着说到这很多人对SWAP其实一直是一知半解的。我当年入行的时候也是只知道“内存不够就加虚拟内存”但为什么要加加了之后系统怎么决定谁去磁盘谁留下完全一头雾水。直到自己真去调优、踩了几次坑才把这套机制搞明白。3.1 虚拟内存、交换分区、交换文件这几个概念先理清我们平时说的虚拟内存在Linux体系里主要有两种实现方式一种是交换分区swap partition就是磁盘上划出一块独立分区专门用来当交换空间另一种是交换文件swap file也就是在已有文件系统上创建一个文件来充当交换空间。交换分区的历史更久很多老教程都让你在装系统的时候就分区好。但现在服务器操作系统都装完了再去调整分区表是一件很麻烦的事搞不好还会丢数据。交换文件就灵活多了你在任意一个目录下创建一个文件把它格式化成swap格式然后启用就是这么简单。我这次用的就是交换文件的方式好处是不破坏现有分区想删除的时候直接删除文件就行方便得很。Windows系统里也有类似的机制就是页面文件那个pagefile.sys就是很多人在Windows上遇到内存不足也扒过这个文件。区别于Windows的是Linux的swap可以独立指定大小也能把多个设备叠加使用在配置上更灵活。3.2 内存、Swap、磁盘三个层级之间是怎么配合的Linux内存管理的最小单位叫内存页一般是4KB大小。当程序申请内存内存页就在物理内存中分配物理内存不够用的时候内核就会把某些内存页搬去Swap空间这个动作叫换出。反之当程序需要访问的数据已经被搬到Swap了内核又得把它弄回物理内存这个动作叫换入。换出谁、不换出谁内核是有算法的。它不像一般人想的“谁最活跃谁就不动”而是要综合考虑页面的访问频率、最近是否被修改过、进程的类型等。我举个简单例子家里客厅面积就那么大你不可能把所有东西都堆在客厅总得有些东西放储物间。储物间就是Swap客厅是物理内存。放储物间是有代价的每次你想用什么东西还得跑一趟储物间。关键点在于哪些东西适合放储物间答案是那些“放了很久都没人碰”的东西。Linux内核里有一种叫LRU最近最少使用的算法来判断页面活跃度现在更精细一点的内核还分活跃页和非活跃页。当你需要释放内存给一个正在快速增长的进程时内核优先从非活跃页里挑页面换出尽量不影响活跃进程。3.3 为什么很多人说Swap一用就卡这个锅该谁背我听到最多的抱怨是“开了Swap之后服务器变卡了”。这个说法对但也不完全对。卡的本质不在于“开了Swap”而在于“换入换出太频繁”以及“磁盘的I/O能力跟不上”。想象一下如果客厅和储物间之间的通道是一条只能容一个人走的窄巷子每次你来回搬东西都得排队。在服务器里这个通道就是磁盘I/O。如果程序访问的数据频繁被换出每次都得到磁盘上去读读一次就得几十毫秒而内存访问是纳秒级的差距是十万倍级别的。真正需要背锅的是不合理的配置和低性能的磁盘。如果你把swap放在一块普通的机械硬盘上又设置了过度积极的swap策略那系统就会频繁触发换页不卡才怪。反过来如果你把swap放在SSD上又设置了合理的触发条件它就能老老实实地当缓冲既不会频繁使用也不会影响主业务的响应速度。我现在这台服务器的磁盘是SSD这一点算是占了便宜。SSD的随机I/O比机械硬盘快很多虽然跟内存比起来还是慢但当紧急缓冲已经够用了。4. 实操为2G服务器创建swap并配置参数理论说得再多不如直接上手操作。下面这整套步骤是我在这台服务器上实际跑过的用的是Ubuntu 22.04环境其他Linux发行版命令基本通用。我把它拆成几个步骤方便跟着做。4.1 准备工作先看清楚当前环境动手之前我习惯先把现状摸清楚。登录服务器之后第一个命令是查看当前内存和Swap的状态free -h这条命令输出里的Swap一行如果total都是0说明你的服务器现在压根没开SWAP。再看一下磁盘剩余空间df -h /为什么要看磁盘剩余因为swap文件要在磁盘上占用一定的空间你得确保有足够的容量。我这边是40G的磁盘用了不到15G剩下的空间完全够用。还有个细节要提醒一下swap文件的大小不要超过系统内存的2倍太多尤其是小内存服务器过大的swap反而会让系统在内存耗尽时一直在磁盘上换页表现得像死机一样。2G内存的机器我建议swap给到4G就封顶了。4.2 创建swapfile的具体步骤第一步用fallocate命令快速创建一个4G大小的文件。有的教程让用dd但在ext4文件系统上fallocate更快它直接分配磁盘块不会真的把4G数据全写一遍。命令如下sudo fallocate -l 4G /swapfile如果报错说fallocate不支持可以退回去用dd会慢一些但兼容性更好sudo dd if/dev/zero of/swapfile bs1M count4096文件创建完之后得把权限收紧。这一步非常重要如果不做系统会警告“swapfile权限不安全”因为一个权限过宽的文件所有人都能读里面可能藏着内存数据碎片虽然没有完整的密码但留着就是个隐患。sudo chmod 600 /swapfile然后把这个文件格式化成swap格式sudo mkswap /swapfile看到类似于Setting up swapspace version 1的输出就说明格式化成功了。接下来启用它sudo swapon /swapfile再用free -h看一眼Swap那一行的total应该就有4G了。到这里宿主机的临时SWAP已经生效但重启之后会失效所以还得配置开机自动挂载。4.3 开机自启配置要让系统每次启动都自动把swap文件挂上需要修改/etc/fstab文件。我一般先备份一下再改sudo cp /etc/fstab /etc/fstab.bak然后在文件末尾加一行echo /swapfile none swap sw 0 0 | sudo tee -a /etc/fstab这里面的四个字段分别表示设备路径、挂载点、文件系统类型、挂载选项和转储顺序。swap没有真正意义上的挂载点所以写none文件系统类型写swap后面的0 0表示不参与备份检查。写完之后可以重启服务器验证一下或者用下面的命令直接模拟挂载sudo swapon --show如果看到输出中包含/swapfile说明配置成功。4.4 内核参数调优swappiness和vfs_cache_pressure说到这我得多说一句很多教程开了swap就不管了这其实不够。核心参数有两个一个叫swappiness一个叫vfs_cache_pressure。swappiness的默认值是60范围是0到100它决定了内核倾向于把内存页换出的积极程度。60这个值意味着当内存可用量低于一定阈值时系统会相对积极地用swap这在2G内存的机器上偏激进。我实际调下来把它设在10到20之间更舒服。设成10意味着只有内存真的紧张到一定程度才开始往swap里倒平时尽量用物理内存。echo vm.swappiness10 | sudo tee -a /etc/sysctl.conf sudo sysctl -p还有个参数叫vfs_cache_pressure控制的是系统对目录和文件元数据缓存的回收倾向。默认100偏高。这类缓存虽然有用但并不是业务进程直接需要的占内存还挺多的。我在2G内存上把它调成50让系统稍微更愿意保留这些缓存但又不会被缓存拖死。echo vm.vfs_cache_pressure50 | sudo tee -a /etc/sysctl.conf sudo sysctl -p调完之后再用free -h观察几天你会发现内存余量会比以前宽裕一点而且swap的占用率低得可怜。这其实就达到了我的目的swap不是拿来天天用的是在关键时刻兜底的。5. 设置之后的运行观察与调优记录配置完成不等于一劳永逸真实运行起来还会有各种意外情况。我把这几个月的观察记录整理一下给大家做个参考。5.1 第一周观察swap到底被用了多少刚把swap加上去的时候我每天都去看一眼free -h的输出。第一天swap使用量是0这说明系统觉得物理内存还够用不需要动用交换区。第三天跑了一个有点重的数据导出任务MySQL和PHP-FPM内存迅速增长这时候我看到swap的used涨到了600多MB。任务跑完大概过了十几分钟内存压力减小系统又把一部分换页数据收回了物理内存swap的used慢慢降下来了。这个现象说明我的配置方向是对的低swappiness值保证了平时不用swap只有真正有临时内存尖峰的时候swap才会介入。而介入之后它确实帮我挡住了好几次潜在的OOM杀进程风险网站再也没有在深更半夜报数据库连接错误了。5.2 日常监控脚本提前预防别等告警才上服务器坦白讲最开始几天我还是有点不放心于是写了个简单的Shell脚本每天凌晨4点跑一次把关键内存信息记录下来。脚本内容大概是这样的#!/bin/bash echo $(date %Y-%m-%d %H:%M:%S) /var/log/mem_usage.log free -m /var/log/mem_usage.log再配合crontab定时任务0 4 * * * /root/check_mem.sh过了一周我翻log文件看内存趋势心里就有底了。除了固定时段的高峰内存占用基本在1.5G到1.7G之间徘徊swap的使用量不超过200MB。这说明系统运行还算健康没有出现那种“依赖swap为生”的恶性循环。5.3 二次排查内存泄漏和僵尸缓存设置swap之后还有一个容易被忽略的问题内存泄漏。有些程序写了不规范的代码申请了内存却不释放时间一长内存占用只涨不跌。一旦这类进程常驻服务器swap再大也不够填。我遇到过一回是某个后台消息推送程序的子进程因为一个循环bug每处理一条消息就多占一点内存。内存占用曲线一直在往上爬要不是看了监控日志根本发现不了。后来我把这个程序重启了一次内存立刻掉下来再后来抽空翻了代码修掉了那个泄漏点。所以我的建议是开了swap之后别觉得万事大吉还是要偶尔执行一下top看一眼哪些进程内存占用异常高。如果发现某个进程的内存持续增长赶紧处理不然再大的swap也只是给泄漏争取拖延时间而已。6. 一年下来的真实收益与使用边界从加上swap到现在已经过去差不多一年了。回头看这个决定我说说真实的数据和感受。6.1 财务收益和稳定性数据这台服务器年付320元如果当初选择升配到4G内存按差价每个月多付40元算一年要多花480元。我通过合理配置swap一分钱没多花把内存不足的问题压住了。稳定性方面过去一年里服务器没有发生过一次因为内存不足导致的进程被OOM Killer杀死的情况。网站访问速度稳定数据库查询没有因为swap产生明显的延迟。我也盯着swap的使用量看了一整年最高峰时它的used值大约在1.8G左右大部分时间都在200MB以内说明它真的只是充当了缓冲池的角色。当然我不是说升级内存不好。如果预算充足升级肯定是更稳妥的方案。但在预算有限的情况下SWAP确实帮我解决了一个很现实的问题一年省下这几百块钱我没有理由不选它。6.2 使用边界什么时候该果断放弃swap这一年的经验也让我对swap的使用边界有了更清楚的认识。如果你发现swap的使用率长时间超过物理内存的一半或者系统的磁盘I/O wait值持续高企那就说明业务的内存需求已经远远超出物理内存的承载能力这时候再靠swap硬撑反而会伤筋动骨。我认识一个朋友他的服务器也是2G内存但他跑了一套完整的ERP系统里面开了一堆Java服务内存常年处在2G满负荷状态。swap加上去之后系统负载反而比没加的时候还高因为Java服务对内存的管理机制会让swap换入换出特别频繁整个服务器的磁盘I/O被拖垮了最后他还是老老实实升级了内存。所以想清楚地知道一件事SWAP是缓冲不是替代。它能帮你扛住偶发性的内存高峰能让你在预算有限的过渡期稳住业务但如果服务器长期运行在内存不足的边缘该升级还是得升级。6.3 我个人建议小内存服务器上加上swap但别指望它万能如果你也是2G甚至1G内存的服务器我的建议是默认就加上swap容量设为物理内存的1到2倍swappiness调低一点然后观察一段时间。这是成本最低、操作最简单、风险最小的一步。但与此同时一定要留意监控数据了解自己真实的内存需求趋势。再加一个实用的小技巧如果在内存紧张时不想让某些关键进程被swap拖慢可以用mlock锁定内存避免进程的内存页被换出。举个例子MySQL的线程池如果被换到磁盘上整个数据库都会变慢这时候在mysqld_safe的启动脚本里加上mlockall参数可以把MySQL锁在物理内存里。不过这个操作要非常谨慎因为被锁定的内存不可交换如果锁得太多会让系统可用的空闲内存变得更少。整个折腾下来我对swap这套机制的理解深了很多不光是会敲几条命令而是真的知道了内存不够用的时候系统在做什么、为什么卡、怎么调才不卡。如果你也正在为2G内存发愁完全可以照着我这套配置试一下大概率能让你的服务器再多撑很久。
返回列表