ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux Swap从原理到调优:配置、监控与故障排查实战

Linux Swap从原理到调优:配置、监控与故障排查实战 服务器半夜OOM数据库进程被内核一刀切了。登录上去一看物理内存8GSwap压根没配页面缓存倒是占了三四个G眼睁睁看着MySQL的buffer pool还没来得及把热数据换进缓存人先没了。这种场景只要做过运维的基本都遇到过。Linux上有多少机器裸奔着跑生产环境就有多少血的教训是亏在Swap上换来的。明明是虚拟内存机制里最基础的一块却常年被忽略要么压根没配要么配了以后swappiness调错方向要么文件大小用dd硬怼结果占用翻倍。这篇就把Swap从原理到实操到调优到排障一次讲透适合刚上手Linux的新人也适合想把手头服务器再抠出点余量的老运维。1. 别急着敲命令Swap在今天的Linux上到底在解决什么问题先花点篇幅把原理讲透很多人吃亏就吃在对Swap的理解停留在内存不够用的备份区。1.1 虚拟内存、物理内存和交换空间的关系CPU访问的是虚拟地址通过MMU内存管理单元映射到物理内存。进程以为自己在独占整个连续空间实际上物理页可能被冲散在内存各处甚至干脆不在物理内存里——这时访问未命中的页会触发缺页异常Page Fault内核指派交换层把数据搬进物理内存如果物理内存不够就先把别的页挪出去腾地方。Swap就是这块被挪出去的落脚点。它在磁盘上内核将暂时不用的匿名页进程匿名内存页写进Swap释放物理内存给更活跃的页。这个机制在Linux上叫交换Swapping和Windows那种整块交投的交换思路完全不同——Linux交换的是页Page粒度小得多所以它能吃下各种内存快满但又不至于立刻死的临界状态。我把这套机制拆成三个动作来理解Page In换入进程访问的页不在物理内存里从Swap或文件系统读回进程阻塞等待。Page Out换出内核线程kswapd把不活跃的匿名页写进Swap腾出物理页。Page Cache页缓存文件系统读过的文件页会留在内存里以备再次读取它们和匿名页争抢物理内存。内核就是要在这个争抢中进行权衡。1.2 没有Swap会怎样——OOM Killer的随机裁决物理内存耗尽时内核的OOM Killer会出手根据oom_score选一个进程杀掉。选谁是个复杂算法不完全按占内存最多的死而是综合进程运行时长、特权级别、占用比例。于是经常出现这种诡异场面一个跑了一周的批处理任务被干掉而你精心守护的Web服务还活着——也可能反过来。很多云厂商默认镜像不给swap理由是云上机器可以随时扩容。这话对vps用户问题不大但在突发流量场景容灾窗口只有几十秒扩容还没来得及生效OOM已经动手了。我见过一个线上故障Java应用一分钟内把机器内存吃满OOM直接把主节点杀了分布式系统加上脑裂的连锁反应整个集群花了三个小时才恢复。事后看监控内存是逐步攀升的其实Swap提前报警完全能兜住。注意我说的是兜住而不是解决。Swap能缓解内存压力但磁盘比内存慢几个数量级如果负载重到Swap也写不动性能照样雪崩。1.3 澄清用了Swap系统就会慢的误区很多人反馈只要swap一启用应用就明显变卡——这个结论对但不全对。变卡的根因通常是两个应用确实把工作集Working Set撑到了超过物理内存换页热点集中在Swap上或者内核换页策略太激进还没到内存不足的程度就把无用匿名页换出去了等进程再访问时重新换入白白多两次磁盘I/O。第一种是容量问题第二种是调优问题。正解不是有Swap就禁用而是让Swap留作应急兜底并且让内核只在真正需要时才用它。这就引出后面要讲的swappiness也是很多人调错的坑。2. 文件方案还是分区方案我的选择逻辑与对比Swap在Linux上主要有三种形式独立分区、文件、zram/zswap。日常工作里争论最多的是分区和文件。2.1 Swap分区与Swap文件的取舍老派教程通常推荐分区理由是性能稳定、不受文件系统干扰。这在大磁盘物理机上没错但到了云主机和容器环境就颠倒了。Swap文件的优势太明显维度Swap分区Swap文件弹性扩容提前规划想扩要动分区麻烦加文件、减文件都随时可做迁移换机器要重做分区直接拷贝文件改fstab性能理论上无文件系统层开销实测差异在5%以内多数负载快照/备份分区不好做文件可纳入备份体系休眠支持原生支持有额外限制但可用实际部署中Swap文件在现代Linux上性能已经非常接近分区了——因为Swap文件的I/O走的是块层加页缓存路径没有普通文件系统那么重的元数据开销内核为它做了专门优化。我做过一次简单的stress-ng压测对比swap分区和swap文件的吞吐差距不到3%日常业务根本感知不到。2.2 什么文件系统不能放Swap文件这里有个硬坑并非所有文件系统都适合承载swap文件。Btrfs在早期版本上swapfile有诸多限制COW、压缩、快照等特性会干扰直到内核5.15及Btrfs较新版本才修复了一部分但依然建议别在Btrfs上做swap文件省得莫名其妙缺页。NFS、FUSE这类网络/用户态文件系统直接不能做swap内核初始化的时候就会拒绝NFS的swap支持是特例多数企业环境用不到。只推荐放在这些文件系统上ext4最稳fallocate和dd两种方式创建都能用xfs生产服务器常见同样稳tmpfs特例tmpfs本质是内存用它做swap等于自欺欺人性能再高也没有意义只适合测试生产环境我习惯用ext4创建前先看一眼df -T确认文件系统类型这块很多人从来没查过结果在Btrfs或者奇怪的文件系统上创建失败排查半天。2.3 什么时候分区依然是更好的选择三种场景我会坚持用swap分区需要休眠Suspend-to-DiskLinux休眠要求swap分区存在并且分区大小至少不小于物理内存文件形式的swap对休眠支持不完整。根文件系统损坏恢复场景系统崩了以后救援模式下swap文件不可用但swap分区可以直接swapon起来作为紧急内存空间。传统LVM环境已经用LVM管理磁盘时建一个lvswap分区成本很低反而是文件方式在LVM的ext文件系统上做多了一层不一定有收益的封装。如果是一台从零开始装的生产物理机我会同时做一个小的swap分区用于休眠/应急一个swap文件用于日常弹性。云主机则一律swap文件因为云盘本身就能弹性扩容没必要维护分区结构。3. 创建Swap文件的分步实操每一步都有讲究下面直接给出一套能落地的操作不只是抄命令每条命令是什么、为什么这样做我都标注清楚。3.1 先估算大小给多少合适Swap大小的经验法则演进过好几个版本。传统红帽推荐物理内存的1到2倍这是内存普遍小于4G时代的经验今天还套用在64G内存的机器上就是灾难——你给两个64G的Swap分区磁盘布局第一个不干第二个真要用上时系统基本已经卡死了。我通常按这个标准评估内存规格Swap建议2G以下内存的2倍2G~8G等于内存大小8G~64G8G~16G64G以上看业务特征一般8G兜底即可做法是保留一个基础额度兜底平时物理内存足够时Swap几乎不增长只有突发内存超卖时才起到缓冲作用。决定好大小后还要看一眼你的磁盘剩余空间别创建完把根分区堵满了。3.2 fallocate还是dd先用快的不行再绕创建swapfile的两种常见方式# 方式一fallocate快秒级分配 fallocate -l 8G /swapfile # 方式二dd慢但兼容性最强 dd if/dev/zero of/swapfile bs1M count8192 statusprogressfallocate更快几兆逻辑块秒批准但在某些文件系统上创建出来的文件可能带洞mkswap时会报 swapfile has holes 并拒绝格式化。ext4和xfs通常没问题Btrfs或网络存储上就容易踩到。稳妥的操作路径是fallocate -l 8G /swapfile # 执行完后先跑一遍 ls -lh /swapfile file /swapfile如果file输出显示/swapfile: cannot open \/swapfile (No such file or directory) 或者提示 sparse file就换dd重来。或者干脆一步到位用dd——要的就是把每个块都真实写入磁盘的确定性。提示无论用哪种方式创建完成后都要检查实际占用的磁盘空间是du -h /swapfile而非ls -lh。因为裸文件被ls看到的可能是逻辑大小而du能看到真实物理占用。如果ls显示8G但du显示只有若干M说明文件是稀疏的内核交换层是拒绝sparse文件的这时得老老实实用dd重写。3.3 权限、格式化和启用# 权限swapfile中包含内存数据必须收紧 chmod 600 /swapfile # 初始化为swap文件系统 mkswap /swapfile # 立即启用 swapon /swapfile # 查看是否生效 swapon --show free -h权限这步不是习惯是安全要求swap文件里会保存内存中驻留过的敏感数据密码、密钥、业务数据chmod 600 属主root能防止普通用户直接读文件。mkswap的输出会显示UUID和文件系统标签记住这个UUID后面fstab要用。一旦执行了mkswap这个文件就不能当普通文件来打开读写了所以要确认路径没问题再执行。3.4 开机自启fstab配置的细节和陷阱要让重启后Swap依然生效需要写入/etc/fstab/swapfile none swap sw 0 0注意一行3个坑Swap文件在root文件系统上所以 /etc/fstab 里挂载根分区的记录必须排在swapfile之前。如果内核在根分区还没挂载时就去启用swapfile结果是找不到文件启动挂起或失败。实际排法是把root行放在最前面swapfile行放在后面。使用UUID更安全避免路径变了导致启用失败。先在fstab里写路径启动一次再用blkid /swapfile | awk {print $2}拿UUID替换路径UUIDxxxx-xxxx-xxxx none swap sw 0 0sw和defaults的区别swap文件用sw是惯例某些发行版用defaults也能挂上因为swap类型的挂载选项本身会忽略大部分文件系统选项。但如果写defaults在某些systemd版本下可能生成不正确的swap单元建议坚持sw。配置完以后不要直接重启验证重启成本高我习惯用这两步验证# 1. 让systemd重新加载fstab并测试配置 systemctl daemon-reload systemctl status swap.swap # 2. 快速验证语法 findmnt --verify --verbose3.5 直接测试一遍重启恢复决定性验证还是真实重启一次。只是晚上人少的时候进行别等到业务高峰期把生产节点重启了。重启后跑free -hSwap那一行应该和重启前一致再跑swapon --show确认source是/swapfile或者对应的UUID设备。4. 真正的调优核心swappiness、cache pressure与换页行为Swap创建完了如果不去调参那只是完成了30%的工作。这一节讲的才是让Swap用得更聪明的核心。4.1 swappiness方向感比数值更重要vm.swappiness默认60范围0-100新内核支持到200。多数人以为它表示物理内存使用超过百分之六十才开始用swap这是最流行的误解。真实含义是内核回收内存时倾向于回收匿名页相对于文件页page cache的权重。值越大越倾向于把进程匿名页换出到swap值越小越倾向回收page cache而不是动匿名页。换句话说不配置的情况下默认60就已经可能在内核觉得page cache更值得保留的时候把匿名页换出去了——所以很多服务明明内存还很充裕却能看到swap用量在涨这不是内存满了是内核策略认为换出冷页腾出page cache更划算。调优方向按业务特征分数据库类MySQL、PostgreSQL、Redis内存里都是热数据换出去再读回来代价极大。建议vm.swappiness10甚至必要时到1。文件服务器、编译机器大量文件访问page cache价值高匿名页相对次要可以维持在默认60甚至往上调。Java应用JVM堆在匿名页里GC又频繁扫全堆换出JVM堆会让GC停顿翻倍。我一般给他10-30视业务抖动情况微调。改法有两种# 即时生效 sysctl vm.swappiness10 # 持久化 echo vm.swappiness10 /etc/sysctl.d/99-swap-tuning.conf sysctl --system我踩过一个大坑在一台有监控采集服务的机器上把swappiness调到0结果监控内存持续缓慢增长但匿名页永远不会被换出最后OOM。原因就是swappiness0不是禁止swap而是尽力避免匿名页换出——内核在极端内存紧张时依然会换出但监控服务的这种慢增长场景它宁可通过其他方式压缩内存也不肯写swap于是累积到OOM。所以调低swappiness的方案尤其适合内存工作集固定的应用不适合内存无限增长但有swap兜底的业务这个界限要分清楚。4.2 vfs_cache_pressuredentry和inode的保留意愿vm.vfs_cache_pressure默认100控制内核回收目录项dentry和索引节点inode缓存的积极性。数值越大越积极回收数值越小越倾向保留。如果一个经常执行ls、find、grep -r、源码编译的机器把这些cache保在内存里能显著降低系统调用延迟。我会调到vm.vfs_cache_pressure50让dentry/inode缓存比page cache更长寿。这是低风险调整比swappiness温和得多试错成本低。改法同上与swappiness一起写进同一个sysctl文件即可。需要提醒的是这个值调到底比如10以下没太大必要——dentry/inode缓存占用太高时反而拖累内存50是一个被很多人验证过的稳妥档位。4.3 让Swap真正空闲下来的时机swapoff的正确姿势有些场景需要彻底把Swap中的数据挪回内存常见于内核配置变更后、对Swap性能不满想重建、或准备缩容释放磁盘。正确的清空命令是swapoff /swapfile但这句话下去系统会把Swap文件里的全部页换回物理内存。如果机器本身内存剩余不够立刻触发OOM。正确的前置操作是# 先看一眼swap用了多少 free -h # 如果占比高不要直接swapoff先给系统留出余量 sysctl vm.drop_caches3 # 回收page cache和dentry/inode腾出物理内存 # 然后回收swap后立即重新挂载 swapoff /swapfile swapon /swapfile还有更稳妥的做法是分两次先把swap用到的量压到不太大比如通过逐步关闭业务进程来释放内存再执行swapoff。如果你是想缩减swap文件大小就swapoff、删文件、重新创建、再swapon如果想整个阶段性地重建我会写一个小脚本先swapoff再创建新的、swapon一步到位。4.4 内核、文件系统和SSD的联动参数补充几个配合维度vm.vfs_cache_pressure加上面说的.vm.min_free_kbytes这个值表示内核保证的可用物理内存低水位。默认值往往偏小在高内存机器上可以适当调高比如保留2%-3%内存好处是给kswapd更早的启动信号让它更从容地回收。但不能太高太高等于浪费内存。fstab里的discard选项如果swap文件放在SSD上可以在fstab的swap选项里加上discardsw,discard或sw再加一行swapon --discard。这会向SSD发送TRIM指令延长寿命代价是轻微的性能损耗SSD上收益更明显。# 临时启用discard不重建swap时 swapon --discard /swapfile5. 用数据说话如何观察、验证和压测Swap配置调优不能靠感觉下面给一套完整的验证链路。5.1 看懂free、vmstat和sar的关键列free -h free -hw # 不带缓存时更直观free -h输出里Swap行的 used/free 只能告诉你用量不能告诉你换页频率。真正盯的是vmstat 1 5里的siswap in和soswap out列vmstat 1 5 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 0 0 4096 8321204 12344 384156 0 0 1 1 2 3 1 0 98 0 0si/so持续非零代表真实换页发生。注意区分so较大表示系统正在把内存页写进swapsi较大表示应用实际读回了swap里的页。只有当si/so频繁且量级高时才需要认真处理——只看到so、几乎看不到si可能只是内核在后台预换页对应用影响远小于直观想象。再看sar -S 1 3它会输出当前swap使用率和换页指标适合留底对比调优前后sar -S 1 35.2 用stress-ng真实压出内存压力我习惯拿一台测试机做压测避免在生产环境裸奔验证# 创建2G swapfile fallocate -l 2G /tmp/swap_test chmod 600 /tmp/swap_test mkswap /tmp/swap_test swapon /tmp/swap_test # 用stress-ng占掉大部分物理内存让swap被动吃压力 stress-ng --vm 2 --vm-bytes 1G --timeout 60s期间另外开一个终端跑vmstat 1 5观察si/so的变化。你会发现内存到一定程度后so开始攀升系统的cpu us下降、wa上升——这就是swap介入时的表现。数据出来后再切换swappiness值对比一次直觉更清晰。5.3 判断Swap是否真的够用看回收、缺页和慢查询业务侧的数据更重要。一条SQL之前20ms最近变200ms了一条Redis GET从微秒级变成毫秒级——才说明问题被业务感知到了。再查看内核统计# 内存回收是否频繁 grep -E pgscan|pgsteal /proc/vmstat # 直接确认swap使用 cat /proc/meminfo | grep -i swap如果 pgscan/pgsteal 数值不断快速上涨说明物理内存长期吃紧。倒推需要的Swap容量时我一般看压力高峰时期的swap使用峰值zabbix/Prometheus都有这个监控项再预留30-50%的余量而不是拍脑袋定8G还是16G。6. 我踩过的Swap深坑三个真实的故障复盘最后用三个亲历的故障收尾比任何教程都有效。6.1 案例一容器宿主机的Swap耗尽导致雪崩某次线上k8s节点出现问题Docker容器内部内存始终没有打满但宿主机swap使用率冲到100%。排查发现容器进程的匿名页被换出到宿主swap当容器内业务再次访问这些页时宿主Swap I/O拥塞整个节点load飙升节点上的Pod集体卡顿。这个故障的根本不是swap不够而是cgroup内存限制没覆盖swap导致容器内存和宿主swap的隔离失效。处理方式分两层先把节点swap调低swappiness10同时在有状态服务的Pod配置里加了内存上限让容器在物理内存边界内完成它的OOM语义而不是让内核把压力传导到宿主机swap。此后批次的大内存Job也提前声明内存申请避免超卖。6.2 案例二MySQL高负载下慢查询激增根因在Swap而不在SQL有一回线上MySQL出现大面积慢查询当时下意识去看慢查询日志、看了索引、看锁排查了两小时一无所获。直到看到vmstat里si列持续在300MB/s以上才意识到问题根本不在SQL而在内存MySQL的buffer pool被换出到swap了。当时那台机器内存64Ginnodb_buffer_pool_size已经是42G还跑着别的Java应用内存超分后又赶上业务高峰swap里全是MySQL的page。后来做了三件事把Java应用迁移走给MySQL独享物理内存innodb_buffer_pool_size 调到物理内存的60%而不是贪满sysctl把swappiness1MySQL侧开启innodb_buffer_pool_in_core_file相关的锁内存配置。这之后再也没有出现过Swap引起的慢查询。6.3 案例三fstab一个配置错误开机卡死在挂载阶段一次在测试环境调整swap文件后重启主机卡在黑屏命令行报 A start job is running for /dev/swapfile 然后超时。原因是fstab里swap条目的UUID写成了旧设备的按UUID找设备找不到systemd的swap单元一直等设备超时。这种问题的修法必须靠串口或管理终端进入emergency mode# 进入emergency模式后 # 代表root文件系统挂载为读写 mount -o remount,rw / # 检查fstab实际生效的设备 blkid # 把错误的swap行注释或改成正确UUID vim /etc/fstab6.4 应急现场备用的三板斧遇到Swap问题来不及细查时按这个顺序先应急# 1. 立即看当前swap和内存水位 free -h vmstat 1 3 # 2. 如果需要快速释放swap内存还有空间时 swapoff -a swapon -a # 3. 临时调整参数进程没那么忙后再把参数持久化 sysctl -w vm.swappiness5最后说一句个人体会Swap从来不是性能加速器它是系统的安全垫和缓冲带价值在于让内核有时间从容回收、让运维有时间介入处理而不是让OOM Killer替你决定谁先死。正确配置Swap 正确监控水位 正确调参能解决的故障比想象中多得多。这套流程我一直在生产环境中使用从没让我失望过。
返回列表