资讯动态

Linux缓存清理正确姿势:理解Page Cache与drop_caches

发布时间:2026/10/13 3:42:39 来源:尧图企业网站定制
你肯定遇到过这种情况一台Linux服务器明明没跑什么重程序用free -h一看内存却几乎被“吃”干净buff/cache占了好几个G剩下那个free的数字往往不到1G。于是你在搜索框里敲下“Linux如何清理缓存”然后看到各种答案有说echo 3 /proc/sys/vm/drop_caches的有说“不用管Linux会自动释放”的还有说“千万别乱清会出事的”。到底哪个对这篇文章就是想把这个问题彻底讲清楚Linux的内存缓存到底是什么哪些情况下值得手动清理哪些命令安全可靠哪些操作会让你越想“省内存”系统越卡。无论你是刚接触Linux的运维新手还是已经带过一批服务器的工程师只要你的机器上跑着Linux这套判断逻辑都适用。在动手敲任何清理命令之前建议先沉下心把前两个章节看完因为绝大多数踩坑都不是命令用错而是对“缓存”这个概念理解偏了。1. 清理缓存前必须搞懂的三个核心概念1.1 Page Cache、Dentry和Inode分别是什么Linux内核里常说的缓存并不是某个应用临时存的“内存垃圾”而是内核主动把磁盘上的数据放到内存里用来加快后续访问。它主要分三类Page Cache、Dentry Cache和Inode Cache。Page Cache最容易理解。你读一个文件内核不是每次直接从磁盘读而是先把文件页放进内存。下次再读同一份数据直接从内存返回速度能差几十倍到上百倍。写入也一样应用写文件时数据先落在Page Cache里内核在后台慢慢刷到磁盘这样应用不用干等磁盘I/O。Dentry Cache和Inode Cache是文件系统的“目录和索引缓存”。Dentry缓存记录的是“某个路径对应的目录项”Inode缓存记录的是文件的元数据比如权限、大小、修改时间。它们不像Page Cache那样“存文件内容”但能大幅减少文件系统元数据的磁盘读取开销。你执行ls大目录、频繁打开关闭文件时命中的差不多就是这两类缓存。如果把这套机制类比成开店备货Page Cache就是“把畅销品直接摆在货架上”Dentry和Inode缓存相当于“店里的商品标签和库存台账”。货架和台账本身也占空间但它们是为了让营业过程更快。Linux缓存占用高本质上是内核在帮你“提前备货”不是内存失控。1.2 free命令的数字该怎么读很多人一看到used达到90%就紧张其实是被free的输出误导了。我建议你以后固定看available这一列。先看一个典型输出total used free shared buff/cache available Mem: 15G 5.2G 1.1G 188M 9.6G 9.0G Swap: 4.0G 0B 4.0G这里free只有1.1G看起来像内存快满了但available还有9.0G。因为内核认为那9.6G的buff/cache大部分可以在新程序需要内存时被回收所以真正“还能分配出去”的量是9.0G。buff/cache里其实包含两部分buff是块设备的缓冲cache主要是Page Cache不过日常排查时不需要分太细把它整体看作“可回收的缓存”基本不会错。available才是考虑了“可回收缓存”后的真实可用量它比free更能代表系统的内存余量。所以看到free很小不等于内存不足先看available。如果available还充足那系统处于健康状态不用想着清理缓存。真正需要警惕的是available持续很低同时Swap在持续上涨。1.3 缓存占用高为什么不等于内存不够还有一个常见误区觉得“缓存占用高 内存浪费”。实际上缓存占用高恰恰是Linux在尽量利用空闲内存。内存如果不用来缓存文件数据就会空在那里“闲置”这对运行在磁盘上的系统是一种浪费。内核有自己的一套回收机制。当新程序申请内存时内核会优先释放低优先级的Page Cache再把内存分配出去。这个过程是自动的通常不需要人为干预。Dentry和Inode缓存同样在内存压力下可以回收只是回收的优先级略低一些。这也是为什么很多老运维说“缓存不用清系统自己会处理”。这句话在绝大多数场景下成立但并不绝对。有些情况下内核的自动回收会产生一种“一边回收、一边缓存又被填满”的抖动或者Swap频繁进出此时手动干预才有意义。我们后面的所有操作都是围绕这类“真正需要干预”的场景展开的。2. drop_caches命令的完整用法与安全边界2.1 动手前先执行sync避免脏数据风险如果确实要手动清理缓存90%的人都会用到/proc/sys/vm/drop_caches。它的原理很简单向这个内核接口写入指定数字内核就会立即丢弃对应的可回收缓存。但在执行之前有一件事必须先做执行sync。syncsync的作用是把Page Cache里的“脏页”尚未写入磁盘的数据强制刷到磁盘。如果跳过这一步直接清缓存写入中的数据就有丢失风险。虽然极端情况下一次正常关机不会立刻丢数据但在实际运维中任何清缓存操作之前先sync是成本极低、收益极高的习惯。执行完sync后清理sudo sh -c echo 3 /proc/sys/vm/drop_caches注意这里的写法。直接把echo 3 /proc/sys/vm/drop_caches放在普通用户shell里会提示权限不足所以要用sudo sh -c把写操作包起来。另外这个proc接口不是所有环境都能用权限受限的容器里会报Operation not permitted这一点第五章节会再展开。2.2 参数1、2、3的选择逻辑drop_caches接口支持三个参数含义必须记清楚参数作用1释放Page Cache文件内容缓存2释放Dentry Cache和Inode Cache文件系统元数据缓存3同时释放Page Cache、Dentry Cache和Inode Cache日常最常用的是echo 1和echo 3。如果只想释放文件内容缓存例如测试“冷缓存”下的磁盘读取性能用1就够如果要模拟冷启动或者确实需要尽量回收内存才用3。不建议一上来就无脑echo 3。清掉Dentry和Inode缓存后内核遇到下一次ls、stat、打开目录等操作时需要重新从磁盘读取元数据文件系统层面的I/O开销会上升。在频繁读写小文件的场景下这种开销非常明显。还有一个容易忽略的点drop_caches写入后不会保留配置值。它不是像vm.swappiness那样的持久化参数而是在你写入一次后“触发一次性动作”动作完成值会自动归零。所以你不要指望把它写进sysctl配置文件里长期生效那是没有意义的。2.3 验证清理效果free和meminfo双确认清理完不能只看几秒钟的数字就说“生效了”最好用两条命令交叉验证。先看整体内存free -h重点对比操作前后的free和available。在Page Cache量较大的机器上执行echo 3后free会明显上升available通常也会小幅上升。如果缓存量本来就不大数字变化不明显也正常因为内核本来就没多少缓存可丢。再看内核的详细统计grep -E Cached|Dentry|Inode|SReclaimable /proc/meminfoCached对应Page CacheSReclaimable是可回收的slab内存Dentry和Inode缓存主要归在这里。清理前记一次值清理后再记一次值对比差值就知道到底释放了多少。我实际操作中的一个小习惯是清理前后各执行一次cat /proc/meminfo把结果存档同时记录当时的磁盘I/O指标。这样如果清理后系统出现性能波动我能立刻判断波动是不是“缓存丢失导致重新读盘”造成的而不是盲目归因。2.4 生产环境可以手动清理缓存的四种场景写到这里必须把话说透生产环境里手动清理缓存是“非常规操作”不是“日常保养”。我见过有人把drop_caches写进每天执行的脚本结果每次执行后数据库都卡顿几分钟。所以需要先分清什么场景下才值得做。第一种场景是压测和性能测试。测试程序要求从“冷缓存”开始跑需要排除之前缓存带来的性能加成这时候手动清一次Page Cache是合理的。第二种场景是需要模拟重启后的表现。比如你想评估一个应用刚启动时的加载耗时不想真的重启整台机器就可以用echo 3把文件相关缓存丢掉让应用冷读一次。第三种场景是内存需要快速让给新进程。比如一台物理机上跑着多个虚拟机或容器宿主机内存确实触顶而你又不能立刻迁移负载这时手动回收缓存能让新进程更快启动。但要注意这只能解决“缓存占着可回收内存”的部分进程真正占用的匿名内存不会被drop_caches碰触。第四种场景是排查疑似“内核缓存失控”的问题。比如slabtop显示回收不正常的Dentry缓存堆积导致内存异常这时手动干预并配合内核参数调整有助于定位问题。除此之外尤其是数据库、Redis、Nginx这类高I/O服务所在的机器不要为了“看到free变大”去主动清缓存。你就是把内存全让出来服务和内核也会立刻把缓存重新填满结果只是制造了一轮不必要的磁盘I/O。3. 不止drop_caches容易被忽略的缓存清理场景3.1 Swap分区回收swapoff和swapon的正确姿势内存缓存之外Swap也是经常需要处理的“伪缓存”占用。Linux在内存紧张时会把不活跃的匿名内存页换到Swap分区。问题是很多内存压力已经缓解的机器Swap里的内容并不会自动吐回内存导致Swap一直占着后续再有内存抖动又来回换页。回收Swap的经典操作是swapoff -a再swapon -asudo swapoff -a sudo swapon -a这个操作的本质是把Swap里的数据读回内存再把Swap清空所以前提必须是当前可用内存足够容纳Swap中已使用的部分。如果机器内存已经很紧张直接swapoff -a可能让系统直接OOM。安全做法是先看Swap使用量free -h如果Swap used接近0回收没什么意义如果Swap used较大先用第二章节的条件判断一下available是否大于Swap used的1.2倍再决定是否执行。稳妥一点的做法可以先回收Page Cache给Swap数据腾出空间再做Swap交换。另外还有个参数值得调配vm.swappiness。它控制内核“倾向于使用匿名内存还是文件缓存回收”的权重默认通常是60。对于多数服务器我建议调到10以下sudo sysctl -w vm.swappiness10写成持久化配置echo vm.swappiness 10 /etc/sysctl.d/99-swap-tuning.conf sudo sysctl --system这样调低后内核会更倾向于回收Page Cache而不是把进程内存换到SwapSwap使用率通常会降下来。3.2 journal日志缓存限制大小比清空更有用很多人不知道systemd的journal日志虽然主要占磁盘但在某些配置下也会让/var/log/journal里的文件不断增长间接影响系统运行。清理方法有两大方向按大小清理或按时间清理。按大小限制例如把日志总量压到200M以内sudo journalctl --vacuum-size200M按时间清理例如只保留7天内的日志sudo journalctl --vacuum-time7d如果希望从源头限制不要每次手工清理可以修改/etc/systemd/journald.confSystemMaxUse100M RuntimeMaxUse50M保存后重启journaldsudo systemctl restart systemd-journald这里欠大家一个提醒重启journald会暂时中断日志记录对高可用日志要求严格的场景建议选择在低峰期操作或者用systemctl kill -s USR1让journald轮转而非直接重启。3.3 包管理器缓存、Docker镜像缓存和临时文件包管理器缓存是又一块容易被忽略的磁盘占用。Debian系用aptRedHat系用yum/dnf清理方式很固定sudo apt clean # 清空已下载的软件包缓存 sudo apt autoclean # 只清理无用的旧包 sudo yum clean all # CentOS 7 / 旧版RHEL sudo dnf clean all # CentOS 8 / Fedora / Rocky这些操作清理的是“软件包安装时下载的缓存文件”不影响已安装软件本身可以放心执行。有些团队还会用dnf autoremove清理不再依赖的旧内核和旧依赖包这是另一个范畴但也值得定期做。Docker缓存同样吃硬盘。镜像、构建缓存、无容器引用的数据卷都会慢慢堆积。先看清楚占用docker system df然后有选择地清理docker system prune-a会删除所有未被容器使用的镜像--volumes会删除未被引用的匿名卷这两个开关都要谨慎。我通常只执行docker system prune不轻易加-a --volumes宁可让它多留一些镜像也不愿意把某个还要恢复的数据卷直接清掉。临时文件方面Linux有systemd自带的定时清理服务systemd-tmpfiles-clean.timer正常情况下不需要手工干预。如果你确实发现/tmp下堆积了大量旧文件可以谨慎地按修改时间清理sudo find /tmp -type f -mtime 7 -delete执行前一定要find /tmp -type f -mtime 7 | head -50先看看命中文件避免误删正在运行中的会话临时文件。3.4 DNS缓存与内核Slab缓存DNS缓存也是“缓存”家族的常见成员。使用systemd-resolved的系统查看统计并清空缓存resolvectl statistics sudo resolvectl flush-caches旧一点的环境可能还是nscdsudo systemctl restart nscd对于DNS缓存日常不太需要主动清理只有在测试域名解析或排查“为什么解析没立即生效”时才用。内核里还有一种叫Slab缓存的机制。它是内核对象如dentry、inode、进程描述符使用的内存在/proc/meminfo里对应Slab、SReclaimable和SUnreclaim。用slabtop可以实时看到哪个slab占内存最多。当你看到某个slab对象数量异常庞大且不可回收时往往不是清理能解决的而是某个内核版本或驱动有内存碎片问题此时应该优先查内核版本和对应配置。4. 用脚本和定时任务实现温和的自动化清理4.1 一个带阈值判断的清理脚本默认不执行drop如果你确实想让清理动作更可控不要直接写“每天echo 3”这种粗暴脚本。下面这个脚本是我在实际环境中验证过的思路只有内存真的紧张时才触发动作而且默认只记录不真正执行drop_caches。#!/usr/bin/env bash # /usr/local/sbin/clean-cache.sh MEM_AVAILABLE_THRESHOLD10 # available百分比阈值 SWAP_USAGE_THRESHOLD20 # swap使用率阈值 AUTO_DROP0 # 0只记录/1才真正执行 LOG_FILE/var/log/clean-cache.log if [ $(id -u) -ne 0 ]; then echo $(date %F %T) please run as root. $LOG_FILE exit 1 fi mem_total$(awk /MemTotal/ {print $2} /proc/meminfo) mem_avail$(awk /MemAvailable/ {print $2} /proc/meminfo) mem_avail_percent$(( 100 * mem_avail / mem_total )) swap_total$(awk /SwapTotal/ {print $2} /proc/meminfo) swap_free$(awk /SwapFree/ {print $2} /proc/meminfo) if [ $swap_total -eq 0 ]; then swap_used_percent0 else swap_used_percent$(( 100 * (swap_total - swap_free) / swap_total )) fi echo $(date %F %T) stat: mem_avail${mem_avail_percent}%, swap_used${swap_used_percent}% $LOG_FILE if [ $mem_avail_percent -ge $MEM_AVAILABLE_THRESHOLD ]; then echo $(date %F %T) memory is enough, skip. $LOG_FILE exit 0 fi if [ $swap_used_percent -ge $SWAP_USAGE_THRESHOLD ] [ $AUTO_DROP -eq 1 ]; then sync echo 3 /proc/sys/vm/drop_caches echo $(date %F %T) drop_caches executed. $LOG_FILE fi # 回收swap前同样要先确认内存余量 if [ $swap_used_percent -ge $SWAP_USAGE_THRESHOLD ] [ $mem_avail_percent -ge 20 ]; then swapoff -a swapon -a echo $(date %F %T) swap reclaimed. $LOG_FILE fi脚本里有几个设计点值得说明。第一默认AUTO_DROP0脚本只会把判断结果写进日志不会真正动缓存。你先跑几天观察日志里统计数值确认阈值合理后再改成1这是最稳妥的上线方式。第二脚本先检查root权限避免某些定时任务用户权限不匹配。第三Swap回收的条件是available至少20%低于这个值绝不硬来。4.2 crontab每天低峰期执行与日志留痕把脚本加入crontab每天凌晨3点执行一次就够不必每个小时跑sudo crontab -e 0 3 * * * /usr/local/sbin/clean-cache.sh /var/log/clean-cache.log 21日志留痕很重要。清理动作如果造成性能波动你事后要有日志可以回溯。脚本里已经带了时间戳和统计值配合crontab本身的重定向输出能清楚地看到每次执行时available和Swap使用率的情况。日志文件本身也要轮转不然一条日志也能把磁盘写满。用logrotate简单配置/var/log/clean-cache.log { weekly rotate 4 compress missingok }这样每周轮转一次保留四周并压缩旧日志。4.3 先看vmstat和slabtop再做决定脚本归脚本真正判断“系统是否需要干预”还是要看几个关键指标。我建议用vmstat 1 5观察5秒vmstat 1 5重点看si和so两列。si是每秒从Swap读回内存的量so是每秒写入Swap的量。如果这两列持续不为0说明系统正在频繁换页内存压力真实存在。此时光清Page Cache未必够需要进一步排查是什么进程占用了大量匿名内存。再配合wa等待I/O看。si/so高且wa高说明Swap和磁盘I/O相互叠加机器已经明显变卡。这时候的处理思路是先定位占用内存最大的进程而不是盯着缓存数字做文章。slabtop -s c可以按缓存大小排序查看Dentry和Inode缓存的占用情况。正常情况下Dentry缓存会在高内存压力下被内核自动回收。如果它始终占据几G且不下降这往往比“Page Cache过大”更需要关注因为Page Cache回收是秒级的而异常slab缓存回收不积极时才可能持续拖住内存。5. 常见问题与排查技巧实录5.1 清理之后可用内存为什么没有戏剧性变化很多人执行完echo 3后发现free是涨了但available变化不大于是怀疑清理没生效。其实这不一定是失败。available在清理前已经把Page Cache视作“可回收的部分”了所以当内核真正释放这些缓存时available数值本身波动很小反而free会明显上升。换句话说available反应的是“系统能给你多少剩余内存”它本来就把缓存算进去了。当你把缓存清掉可用内存只是从“可回收”变成“已经空闲”总量上没有本质变化。只有在内存压力大、内核又来不及回收缓存时主动清理才会让available出现肉眼可见的上升。所以验证清理效果重点看两个点一是free是否上升二是清理前的buff/cache数量有多大。如果buff/cache只有几百M清理后没变化完全正常。5.2 清理缓存后系统反而更慢了我踩过最典型的坑是在一台线上Nginx代理机上执行了echo 3。当时机器的buff/cache很大我看着free数字变大很开心结果接下来半小时磁盘I/O持续飙升请求响应时间明显变长。原因很简单Nginx访问的静态文件之前都在Page Cache里我一个命令全部清掉每个请求都得重新去读磁盘。这不是命令“有毒”而是我没考虑业务对文件缓存的依赖。高并发读文件的场景下Page Cache本身就是性能支柱。清掉它等于把本来高效的内存访问降级为低速磁盘访问数据要重新“预热”回来这段时间系统自然更慢。所以在高I/O业务上我的建议是不要手动清理Page Cache让内核自己调度。如果因为压测等特殊原因必须清理提前跟业务方打招呼并且选择访问低峰期尽量用echo 1而不是echo 3。5.3 哪些情况千万不要手动清理有几类场景手动清理缓存风险极高遇到了一定要克制。第一类是内存已经严重不足机器都开始OOM或者接近OOM时。此时进程的匿名内存占了绝大部分drop_caches能释放的只是很小一部分缓存反而可能因为清掉缓存后触发大量集中读盘让系统更加不稳定。第二类是数据库所在机器。数据库通常有自己的Buffer Pool不属于内核Page Cache。但它的数据文件读取也会占用系统层缓存。你手工清理后数据库下次查询同样要重新读盘性能抖动很难避免。第三类是容器内没有权限还要执行。容器内执行echo 3 /proc/sys/vm/drop_caches往往会报Read-only file system或权限拒绝。即使容器没被Seccomp拦住这个操作影响的也是宿主机全局缓存会让同主机的其他容器一起“降速”。正确的做法是在宿主机层面评估不要从容器里操作。第四类是频繁清理。一天清好几次缓存会让系统永远处于“缓存没来得及积累就被清掉”的状态磁盘I/O持续偏大整体性能反而下降。缓存的价值就在于复用来命中热数据频繁清掉等于把热数据反复降温。5.4 判断内存压力的正确思路最后分享一套实际判断流程。我不再因为free小就紧张而是按下面的顺序排查先看free -h的available低于20%才进入下一步再看vmstat的si/so如果Swap换页持续存在说明内存压力已经影响到性能接着用ps aux --sort-%mem找出占用内存最大的进程最后确认是否存在内存泄漏或配置不合理而不是急着清理缓存。另外如果你想让内核调整dentry和inode缓存的回收倾向但又不想手动清理可以用vm.vfs_cache_pressure这个参数。默认值是100数值越大内核越积极地回收目录和元数据缓存数值越小越倾向于保留它们。echo vm.vfs_cache_pressure 50 /etc/sysctl.d/99-cache-tuning.conf sudo sysctl --system这样就温和地告诉内核“元数据缓存尽量多留一会儿。”它不是清理动作而是通过调节回收偏好来缓解缓存侧压力。我在实际维护中慢慢养成了一种习惯碰到“Linux缓存”这个问题先不急着找清理命令而是先回答三个问题——业务是否依赖文件缓存available是否真的不足Swap是否在反复进出如果答案都是否那这件事就不该做。清理缓存不是为了让某个数字变得好看而是为了让系统在真实的内存压力下还能平稳运行。你愿意折腾它不如先理解它很多本不该发生的故障都是因为一句“先清一下缓存”引发的。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑