系统卡顿的时候,多数人会下意识打开监控面板看一眼,然后盯着“交换分区可用率”的数字发呆:它明明还剩下不少,为什么服务就是慢吞吞的?等哪天真见底了,又慌忙加交换空间,结果过一阵子同样的问题再次出现。我之前在某项目的服务器上也遇到过类似的情况,折腾几次之后才明白一件事:交换分区可用率不是单纯的“剩余容量”,它反映的是整条内存回收链路的健康程度。这篇文章就从这串数字说起,把影响可用率的底层机制拆开,再给出完整的诊断、调参和验证流程,最后分享一些踩坑记录。无论是刚入行的运维新人,还是被“内存明明够用却卡的厉害”折磨过的开发,都能找到可以直接照做的操作。
1. 可用率这个指标,看清它才算开始优化
1.1 一个百分比背后是整条内存回收链路的状态
如果只看公式,交换分区可用率就是“当前可用的交换空间除以交换空间总量再乘以100%”。但这个表面简单的百分比,其实是物理内存、内核内存回收机制、存储设备速度和进程内存分配习惯共同作用的结果。
先说最常见的一种情况:可用率持续走低。物理内存里的匿名页,也就是进程的堆、栈、数据段这些内容,在内存压力变大时会被内核换出,写入到交换分区或者交换文件里。这个过程不断发生,可用率自然就一点点降下来。与此同时,内核的后台回收线程kswapd会持续工作,如果回收速度跟不上应用程序的分配速度,可用率不仅会快速下降,还会带出一连串连锁反应——系统load升高、IO wait显著爬升,最终表现为用户能感知的卡顿。
这个过程可以类比成仓库管理。货架是物理内存,仓库外的临时棚是交换分区。货架放不下新货时,管理员得把一部分不常用的货搬到临时棚;临时棚剩余空间就是可用率。可用率越低,说明搬到外面的货越多。如果管理员在货架与临时棚之间来回跑,搬运工作本身就会占用大量人力,对应到系统上就是IO和CPU的额外开销。
1.2 空间不足和回收效率差,是两个完全不同的故事
很多人在看到可用率接近零时,第一反应是“空间不够,加大”。但加大交换空间之后,如果可用率依然周期性触底,说明问题根本不在容量,而在回收效率。
回收效率差的典型症状是:物理内存明明还剩不少,系统却一直在换页。这通常和内核的水位阈值watermark设置不当,或者page cache占用过多内存导致回收迟钝有关。还有一种情况是页面的集中换出,形成小而密集的IO突发,直接拖慢应用的响应时间。
我自己的排查习惯是把这两种情况分开判断:
- 如果交换分区使用量从0缓慢爬升,直到触顶,同时物理内存的free长期偏高,那优先怀疑回收参数配置不合理。
- 如果交换分区瞬间被打满,同时内存的available快速下降,那通常和应用层有关,比如某个进程发生了内存泄漏,或者瞬间申请了一大批内存。
这两类问题对应的解决路径完全不同。前者调系统参数就能缓解,后者可能得改代码、换配置,甚至直接加物理内存条。
1.3 可用率曲线的形态,也能告诉你很多信息
把可用率按时间画成一条曲线之后,能比较直观地看出三形态态。
| 曲线形态 | 可能的系统状态 | 建议动作 |
|---|---|---|
| 缓慢下降后稳定在低位 | 长期内存压力适中,系统靠交换分区维持运行 | 优化常驻进程的内存占用,或适度增加物理内存 |
| 周期性强波动 | 定时任务集中执行,内存突发占用后释放 | 给任务错峰,或者用cgroup限制任务的内存上限 |
| 断崖式下跌 | 某个进程异常申请内存或发生内存泄漏 | 定位异常进程,检查监控日志,必要时重启服务并修复根因 |
通过曲线形态做初步判断,能省下大量翻日志的时间。尤其是周期性强波动那种情况,我在好几个项目里都见过,最后排查下来几乎都是凌晨的某个统计任务或备份任务造成的。这类问题只要错峰或者限制内存,就能立竿见影。
2. 影响可用率的四个变量,别只盯着swappiness
2.1 swappiness到底控制的是什么
只要在网上搜过“交换分区优化”,十有八九会看到“修改swappiness参数”的建议。这个参数确实重要,但很多人对它的理解是有偏差的。
swappiness的默认值通常为60,取值范围是0到100。它控制的是内核在回收内存时对匿名页和文件缓存页的“偏好程度”,数值越大,越倾向于把匿名页换到交换分区;数值越小,越倾向于回收文件缓存页。注意,这里说的是“倾向”,不是“非黑即白”的开关。即使把swappiness设为0,在内存极度紧张、文件缓存又无法满足回收需求时,内核依然会换出匿名页。
很多优化文章建议直接改成10甚至0,理由是减少换页就能提升性能。这个建议存在一个隐含前提:物理内存足够大,而且应用对文件缓存的依赖不强。如果物理内存本就不足,强行把swappiness调低,反而会导致文件缓存被大量回收,数据库、日志类应用可能会因为缓存命中率下降,出现更频繁的磁盘读取,性能不升反降。
我曾经在一台内存只有4GB的服务器上踩过这个坑。当时把swappiness从默认值改成10,结果数据库的查询延迟反而上升了,仔细一看,伤的是文件缓存。所以参数不是越低越好,得结合内存总量和应用类型来判断。
2.2 存储硬件:交换分区跑在HDD上,体验天差地别
交换分区可用率本身不包含速度信息,但底层存储介质的速度直接影响可用率变化带来的体感。
当交换分区位于机械硬盘上时,换页操作意味着磁盘寻道和旋转延迟,IO时间往往以毫秒到十几毫秒计。高负载下,大量换页会让磁盘长时间处于忙碌状态,这是系统卡顿的最直观来源。相比之下,SSD或者NVMe固态硬盘的随机读写能力远超HDD,交换分区放在上面,换页的开销会小一个数量级。
这不是说机械硬盘就不能用交换分区,而是说当物理内存长期不足时,放在HDD上的交换分区会放大性能损失。我之前运维过一台旧的物理机,内存8GB,交换分区在机械盘上,白天高峰期数据库操作频繁,IO wait经常飙到30%以上。后来把交换分区迁移到一块SSD上,同样负载下IO wait降到了10%左右,整机的响应速度明显变好。
如果条件允许,建议优先考虑用NVMe固态的一部分空间来做交换分区。但要注意,频繁的换页写入会加速固态盘寿命的消耗,所以从长远角度看,如果应用内存需求持续增长,最根本的解决方式仍然是增加物理内存。
2.3 应用负载与内存分配模式
交换分区可用率的变化,不能脱离应用负载来谈。不同类型的应用,对内存的使用模式差异巨大。
常驻型的服务,比如Web服务、应用服务器,内存占用一般比较稳定,可用率变化幅度小。而批处理型任务,比如数据分析、日志压缩、定时同步,往往在短时间内申请大量内存,处理完毕之后又释放,很可能造成可用率的短期快速下降。
这两种负载需要采用不同的策略。常驻型服务要关注的是“稳定的内存水位”,如果可用率长期偏低,就要评估是否扩容内存或者优化服务内存占用。批处理型任务则更适合用cgroup将其限制在一定的内存范围之内,避免突发性的内存申请把系统整体内存节奏带乱。
这里的关键是区分“平均内存压力”和“瞬时内存压力”。平均值不高但瞬时峰值很高的系统,其实不一定需要加内存,错开任务时间、给任务设限额,往往更直接有效。而平均值持续高位的系统,该扩容还是得扩容,靠调参数很难从根本上解决问题。
2.4 监控的采样周期,决定了你能不能发现问题
优化完参数之后,很多人会用free命令看一眼,觉得“行了”,然后就收工。但我想说的是,单次命令的输出只能反映瞬间状态,无法描绘可用率的变化规律,也就难以判断优化是否真正生效。
监控平台的采样周期同样很重要。如果你的监控是5分钟一次甚至更长,那些几十秒内发生的断崖式下跌可能完全被平滑掉,问题只会在某一天突然以故障形式爆发出来。
我一般建议对交换分区相关指标采用“秒级采样加分钟级聚合”的思路。至少要有每秒采样的实时视图,同时保留1分钟和5分钟粒度的历史数据用于回溯。关注的指标不只是可用率本身,还包括swap in和swap out的速率。这两个速率直接反映系统每秒从交换分区读入和写入的数据量,它们才是判断“系统是否频繁换页”的最直接证据。
| 指标 | 含义 | 参考阈值 |
|---|---|---|
| swap in速率 | 每秒从交换分区读入的数据量 | 长期大于数十MB/s时需关注 |
| swap out速率 | 每秒写入交换分区的数据量 | 长期大于数十MB/s时需关注 |
| 可用率 | 可用的交换空间比例 | 长期低于20%时需关注 |
3. 优化操作的完整链路:诊断、调整、验证
3.1 诊断阶段的五条命令和它们怎么看
真正动手优化之前,先把现场摸清。我一般会按顺序执行下面这几条命令,每一条都能提供不同的视角。
free -h这条命令看的是内存整体情况,重点是available这一列。它表示当前在不触发换页的情况下还能分配多少内存,比free更能反映实际可用量。
cat /proc/swaps swapon --show这两条命令用来确认交换分区或交换文件的实际挂载情况,包括类型、大小和当前使用量。如果你有多个交换设备,这里能看到各自的状态。
vmstat 1 10这条命令是核心。它每秒输出一次系统状态,在si和so列你会看到swap in和swap out的速率。如果so长期不为0,说明系统持续在写交换空间;si经常有波动,说明有数据从交换空间读回来,这都说明内存压力客观存在。
cat /proc/sys/vm/swappiness查看当前swappiness值。注意这只是“当前运行值”,真正开机生效的值可能在别的配置文件里。
看这些输出的时候,我建议养成一个习惯:把free里面的available和vmstat里的si、so对照起来看。如果available充足但si值很高,说明系统可能在无谓地换页,这是回收策略问题。如果available很低且si、so都很高,说明确实是内存不足,接下来要考虑扩容或者优化应用。
3.2 根据系统形态设定核心参数
诊断完成之后,我们进入调参阶段。
先看内存总量和负载特点。内存充足,也就是系统平均内存利用率在60%以下,且swap in/out极少,这种情况下可以把swappiness调低到10左右,减少不必要的换页,让文件缓存发挥更大的作用。
内存不足时,情况相反。平均利用率长期超过80%,交换分区承担了实际作用,此时不建议强行压低swappiness。更好的做法是先保留默认值或者适度调低到30,优先解决应用内存占用问题。参数可以缓解症状,但解决不了物理资源短缺的本质问题。
还有一类场景:系统跑的是数据库、缓存服务这类对响应时间很敏感的应用。这类服务通常希望热门数据全部在物理内存中,我实践中习惯把swappiness设置到更低,且需要保证文件缓存得到保护。但前提是压测验证过,不完全依赖理论判断。
除了swappiness,还有几个关联参数值得检查。比如vm.min_free_kbytes,它控制着内核保留的最低空闲内存量。设置得太小,可能导致系统在内存快满时触发频繁回收;设置得太大,又会让太多内存成为“保留区”,白白浪费。常规做法是先保持默认,在明确诊断出回收抖动时再调整。
3.3 动态调整与持久化,两个步骤缺一不可
参数调整可以动态进行,不需要重启。
sysctl -w vm.swappiness=10这条命令立即生效,只对当前运行状态起作用,重启后会恢复默认值。所以还需要做持久化配置,把参数写入配置文件。
echo "vm.swappiness=10" >> /etc/sysctl.conf sysctl -p这里有个容易踩的坑:不同发行版对sysctl配置文件的优先级处理不同。有的环境会读取/etc/sysctl.d/目录下的独立文件,如果同时存在多个文件配置了同一参数,后加载的可能会覆盖先加载的。所以持久化之前,先检查一下现有的配置文件,避免参数冲突。
我自己的习惯是在/etc/sysctl.d/目录下新建一个专门的文件,比如99-swap-tuning.conf,把交换分区相关的调优参数集中放在里面。这样既清晰,又不会被其他配置覆盖。执行sysctl -p之后,再用cat /proc/sys/vm/swappiness确认实际值。
3.4 验证:用三个指标确认优化确实生效
参数调整完,最怕的就是“凭感觉觉得快了”。验证是否真正有效,至少要盯三个指标。
第一个是swap in和swap out速率。优化后,这两个速率应当出现可观察的下降,尤其是so列应该显著变少。如果速率不变,说明参数调整力度不够,或者问题不在回收策略上。
第二个是IO wait的变化。使用vmstat或者系统监控工具观察wa列。如果优化有效,IO wait应当下降,特别是之前高负载的场景中,这个改善会非常明显。
第三个是应用侧的实际响应时间,比如接口的P95延迟、数据库查询耗时。这是最终的用户体验标准。参数调得好不好,最终还是由应用指标说话。
我建议在调整后进行至少一个完整业务周期的观察,比如观察24小时。因为内存回收行为在短期负载波动下未必能真实反映出变化,只有经过一次完整的高峰和低谷循环,数据和结论才靠谱。
4. 进阶玩法:交换文件、zram和进程级限制
4.1 用交换文件替代传统分区
很多系统在安装时不会单独划分交换分区,而是使用交换文件,这在现代发行版里已经很常见。相比传统分区,交换文件有几个明显的好处:不需要提前规划分区大小,想扩就扩,想缩就缩,迁移也更灵活。
创建一个交换文件的流程其实不复杂:
fallocate -l 4G /swapfile chmod 600 /swapfile mkswap /swapfile swapon /swapfile用fallocate创建文件之后,必须执行chmod 600,否则内核会拒绝将其作为交换设备使用,因为它担心权限过宽导致其他用户读取敏感内存数据。这一步很容易被忽略。
持久化则是在/etc/fstab中添加一行:
/swapfile none swap defaults 0 0交换文件适合临时扩容的场景。比如你买了一台云服务器,物理内存8GB,应用升级后内存需求升到12GB,短期又不想升级套餐,这时候加一个交换文件是最稳妥的临时方案。
但要注意,如果交换文件所在的文件系统本身已经很繁忙,换页操作会和普通读写争抢IO,效果可能比传统交换分区还差。所以条件允许时,最好放单独的数据盘上。
4.2 zram:让换页发生在压缩内存里
交换分区可用率优化的一个进阶方向,不是减少换页,而是让换页过程本身变快。zram就是这么一种方案:它在物理内存中划出一块区域,把换出的页面压缩之后放在里面。
现代CPU的压缩和解压缩速度都很快,内存数据压缩率通常在2:1到3:1之间,相当于在内存里凭空多了一半以上的可用空间,并且换入换出不经过磁盘,延迟远低于任何存储设备。
我在低内存的设备上试过zram方案,比如一些只有2GB内存的迷你服务器,开启zram之后,系统的可运行内存接近翻倍,而且卡顿明显减少。zram的使用方法也比较简单,很多发行版都有现成的模块和工具,加载模块后创建一个zram设备,设置大小并启用即可。
modprobe zram echo 2G > /sys/block/zram0/disksize mkswap /dev/zram0 swapon /dev/zram0zram适合的场景是物理内存本身就很小、且对IO延迟敏感的设备。但如果物理内存足够大,zram的意义就不太大了,因为压缩和解压本身也有CPU开销,没必要为了一个不太需要的功能增加无谓的消耗。
4.3 cgroup内存限制:把交换行为锁在特定进程范围内
有时候问题不在全局,而是某一个进程的异常内存增长拖累了整机。此时用cgroup来限制特定进程的内存和交换行为,比全局调参更精准。
以cgroup v2为例,可以用systemd-run启动一个限制内存的服务:
systemd-run --unit=my-limited-service --scope -p MemoryMax=2G -p MemorySwapMax=500M -- /path/to/your-commandMemoryMax限制物理内存上限,MemorySwapMax限制该进程的交换空间上限。这样进程的物理内存用完之后,最多只能再使用500M的交换空间,一旦超出就会被OOM Killer选中,避免它把整台机器拖垮。
我有一次排查可用率周期性跳水问题时,发现某个日志处理任务的突发内存申请会在十几秒内吃掉3GB以上的内存,导致整机进入频繁换页状态。后来用cgroup把它限制在2GB内存和1GB交换空间以内,任务运行时间虽然拉长了一点,但整机的稳定性好了很多。
这种做法的核心价值在于“隔离风险”。系统里有上百个进程,没必要为一个进程的异常行为承担全局性能下降的代价。
5. 我在实际运维中踩过的坑和总结的经验
5.1 “交换空间越大越稳”是我最早栽过的跟头
刚开始接触服务器运维那阵子,我拿到一台新机器就习惯性地把交换分区设得特别大,觉得空间越大越不容易出现“交换空间不足”的报错。后来线上出过一次问题才明白,交换空间大并不等于系统稳定,甚至可能掩盖真正的问题。
那段时间某个项目服务器的物理内存是16GB,交换分区被设成了32GB。平时看起来一切正常,但高峰期时系统变得异常缓慢。排查下来才发现,由于内存压力没有及时暴露,系统大量换页到交换分区,IO等待时间长到离谱。由于可用率始终充足,这个隐患被隐藏了很久,直到用户反馈才被发现。
物理内存不足时,交换分区再大也只是“慢性失血”,它能延缓崩溃,但不能解决问题。后来我把那台机器的物理内存扩容到32GB,交换分区降到8GB,高峰期响应反而快了很多。这个经历让我养成了一个习惯:交换空间本身并不需要特别大,够用就好,真正关注的应该是物理内存是否满足应用的正常需求。
5.2 直接禁用交换分区,省下的速度得拿稳定性还
有人听说“禁用交换分区能提速”,于是直接执行swapoff -a把交换分区全关了。在一些内存充足的机器上,这个操作确实不会造成明显问题,但在另一些场景下,代价会非常大。
我曾经给一台内存使用率在85%左右的服务器做过这种操作,本意是想减少换页,结果不出半小时,系统的OOM Killer开始频繁触发,一些占用内存较多的后台进程被逐批杀掉,直接影响了业务稳定性。原因是物理内存不足时,内核没有交换分区作为缓冲,只能靠强制回收和OOM机制来释放内存,这比换页带来的性能损失大得多。
正确做法不是完全禁用交换分区,而是合理设置参数,让它“能不用就不用,但关键时候能顶上”。把swappiness调到较小的值,同时保留适量的交换空间,这才是更稳妥的平衡点。
5.3 一次“可用率周期性跳水”的完整排查记录
最后分享一次具体的排查过程。当时某项目的运维群里有人反馈,每天凌晨两点左右,服务器的响应速度会出现明显的下降。监控面板上,交换分区可用率在凌晨两点前后从80%急速下落至个位数,随后在十几分钟内恢复。
第一反应是物理内存不足,于是加了物理内存。但第二天同一时间,现象依旧。显然,问题不在于容量,而在于某个周期任务触发的大量内存申请。
排查过程如下:先在监控平台上确认进程维度的内存占用曲线,果然发现某个数据同步进程在凌晨两点整启动。接着查看该进程的内存申请量,一个批次会集中申请大量内存,之后又释放。
处理方案有三步。第一步,用cgroup给这个进程设置内存上限,避免它一次性申请过多内存。第二步,在任务脚本里调整批次大小,把一次性的大内存申请拆成多次小批处理。第三步,将同步任务从凌晨两点改到业务低谷时段,避开其他备份任务。
这三步之后,交换分区可用率的跳水现象就再没出现过。这个案例给我最大的启发是:可用率指标异常,本质上只是一个“症状”,真正的病因往往需要结合监控、进程分析和业务调度才能定位。
最后的话
关于交换分区可用率,我个人折腾几年之后最深的体会是:别把这个数字当孤立的容量指标看,它背后连着物理内存水位、内核回收策略、存储介质速度和应用负载特征。真正有效的优化,不是一上来就调参数,而是先把数据看透,再根据系统实际情况动手。手上的工具永远够用,缺的往往是冷静的判断。希望大家在优化自己的系统时,少走一点我走过的弯路,让服务器在关键时刻真正扛得住。