交换分区可用率越低系统越卡?从内存回收到参数调优的完整排查指南
系统卡顿的时候多数人会下意识打开监控面板看一眼然后盯着“交换分区可用率”的数字发呆它明明还剩下不少为什么服务就是慢吞吞的等哪天真见底了又慌忙加交换空间结果过一阵子同样的问题再次出现。我之前在某项目的服务器上也遇到过类似的情况折腾几次之后才明白一件事交换分区可用率不是单纯的“剩余容量”它反映的是整条内存回收链路的健康程度。这篇文章就从这串数字说起把影响可用率的底层机制拆开再给出完整的诊断、调参和验证流程最后分享一些踩坑记录。无论是刚入行的运维新人还是被“内存明明够用却卡的厉害”折磨过的开发都能找到可以直接照做的操作。1. 可用率这个指标看清它才算开始优化1.1 一个百分比背后是整条内存回收链路的状态如果只看公式交换分区可用率就是“当前可用的交换空间除以交换空间总量再乘以100%”。但这个表面简单的百分比其实是物理内存、内核内存回收机制、存储设备速度和进程内存分配习惯共同作用的结果。先说最常见的一种情况可用率持续走低。物理内存里的匿名页也就是进程的堆、栈、数据段这些内容在内存压力变大时会被内核换出写入到交换分区或者交换文件里。这个过程不断发生可用率自然就一点点降下来。与此同时内核的后台回收线程kswapd会持续工作如果回收速度跟不上应用程序的分配速度可用率不仅会快速下降还会带出一连串连锁反应——系统load升高、IO wait显著爬升最终表现为用户能感知的卡顿。这个过程可以类比成仓库管理。货架是物理内存仓库外的临时棚是交换分区。货架放不下新货时管理员得把一部分不常用的货搬到临时棚临时棚剩余空间就是可用率。可用率越低说明搬到外面的货越多。如果管理员在货架与临时棚之间来回跑搬运工作本身就会占用大量人力对应到系统上就是IO和CPU的额外开销。1.2 空间不足和回收效率差是两个完全不同的故事很多人在看到可用率接近零时第一反应是“空间不够加大”。但加大交换空间之后如果可用率依然周期性触底说明问题根本不在容量而在回收效率。回收效率差的典型症状是物理内存明明还剩不少系统却一直在换页。这通常和内核的水位阈值watermark设置不当或者page cache占用过多内存导致回收迟钝有关。还有一种情况是页面的集中换出形成小而密集的IO突发直接拖慢应用的响应时间。我自己的排查习惯是把这两种情况分开判断如果交换分区使用量从0缓慢爬升直到触顶同时物理内存的free长期偏高那优先怀疑回收参数配置不合理。如果交换分区瞬间被打满同时内存的available快速下降那通常和应用层有关比如某个进程发生了内存泄漏或者瞬间申请了一大批内存。这两类问题对应的解决路径完全不同。前者调系统参数就能缓解后者可能得改代码、换配置甚至直接加物理内存条。1.3 可用率曲线的形态也能告诉你很多信息把可用率按时间画成一条曲线之后能比较直观地看出三形态态。曲线形态可能的系统状态建议动作缓慢下降后稳定在低位长期内存压力适中系统靠交换分区维持运行优化常驻进程的内存占用或适度增加物理内存周期性强波动定时任务集中执行内存突发占用后释放给任务错峰或者用cgroup限制任务的内存上限断崖式下跌某个进程异常申请内存或发生内存泄漏定位异常进程检查监控日志必要时重启服务并修复根因通过曲线形态做初步判断能省下大量翻日志的时间。尤其是周期性强波动那种情况我在好几个项目里都见过最后排查下来几乎都是凌晨的某个统计任务或备份任务造成的。这类问题只要错峰或者限制内存就能立竿见影。2. 影响可用率的四个变量别只盯着swappiness2.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.swappiness10这条命令立即生效只对当前运行状态起作用重启后会恢复默认值。所以还需要做持久化配置把参数写入配置文件。echo vm.swappiness10 /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 --unitmy-limited-service --scope -p MemoryMax2G -p MemorySwapMax500M -- /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给这个进程设置内存上限避免它一次性申请过多内存。第二步在任务脚本里调整批次大小把一次性的大内存申请拆成多次小批处理。第三步将同步任务从凌晨两点改到业务低谷时段避开其他备份任务。这三步之后交换分区可用率的跳水现象就再没出现过。这个案例给我最大的启发是可用率指标异常本质上只是一个“症状”真正的病因往往需要结合监控、进程分析和业务调度才能定位。最后的话关于交换分区可用率我个人折腾几年之后最深的体会是别把这个数字当孤立的容量指标看它背后连着物理内存水位、内核回收策略、存储介质速度和应用负载特征。真正有效的优化不是一上来就调参数而是先把数据看透再根据系统实际情况动手。手上的工具永远够用缺的往往是冷静的判断。希望大家在优化自己的系统时少走一点我走过的弯路让服务器在关键时刻真正扛得住。