ARTICLE DETAIL

资讯详情

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

Linux内存缓存解析:Page Cache、dentry/inode与yum/apt清理实践

Linux内存缓存解析:Page Cache、dentry/inode与yum/apt清理实践 每次线上服务器因内存使用率过高触发告警群里第一张截图永远是free -h。常见场景是这样的机器明明没什么业务流量内存却显示被吃掉一大半used高、free见底马上就有人开始喊“要不要清一清缓存”。我的态度一直是先别急着动搞清楚这些内存究竟被拿去做了什么再说。这就要回到Linux缓存这件事本身。不管你是自己排查还是顺手问一句元宝、DeepSeek这类AI助手得到的结论基本是一句话Linux系统的缓存主要分两类一类是内存里的Page Cache、dentry、inode另一类是yum/apt这类软件包管理器落盘的文件缓存。结论好背但真正排障时能讲清楚这些缓存各自是什么、什么情况下能清理、什么情况下千万别碰的人其实不多。这篇文章就把这套东西拆开讲透。会覆盖三大内存缓存的工作原理、内核回收机制、yum/apt缓存的日常管理与离线仓库搭建最后补几个我在生产环境里踩过或处理过的真实案例。刚入门的人能看懂底层原理老手也能找到可以直接抄走的操作。1. 先搞清楚Linux在内存里缓存的三样东西1.1 Page Cache最直观的“文件副本”Page Cache是大多数人印象里的“缓存”也是最能吃内存的部分。它的核心逻辑一句话把磁盘上的数据在内存里留一份副本后续读写直接操作内存减少磁盘IO。读文件时进程发起read()系统调用内核先去Page Cache里找对应的数据页。命中了就直接返回给用户态没命中才分配物理内存页、发起磁盘IO把数据读进来同时放进Page Cache。写文件时也一样进程调用write()数据先写进Page Cache把这一页标记成脏页Dirty真正落到磁盘是后面后台线程的事。如果短时间内大量写文件你能在/proc/meminfo里看到Dirty和Writeback两个字段明显上涨这就是脏页在排队落盘。用生活里的事打比方Page Cache就是厨房台面。你做饭不会每次要用葱姜蒜才跑一趟菜市场而是提前备料放台面上台面就是内存菜市场就是磁盘。台面足够大、备料合理做饭自然快但台面如果被一堆临时用不上的东西占满反而影响操作。Linux让你觉得“内存不够”的那种状态其实很像台面被备料占了一大半——但你真正要用的锅碗瓢盆应用程序需要的内存内核有专门的回收机制腾地方。在free -h输出中buff/cache这一列是Page Cache、块设备缓冲以及slab可回收部分的近似总和具体对应/proc/meminfo里的Buffers Cached SReclaimable。其中Buffers是块设备层面的缓冲Cached主要是文件页缓存也就是Page Cache的主体。1.2 dentry和inode比Page Cache更隐蔽的内存消耗Page Cache缓存的是文件内容但内核要访问一个文件光有内容远远不够。每次打开文件前内核必须顺着路径做解析。比如打开/etc/nginx/nginx.conf它要逐层解析etc、nginx、nginx.conf这三个路径分量。这个解析过程靠的就是目录项缓存dentry cache和索引节点缓存inode cache。dentry cache缓存的是目录项负责把文件名映射到对应的inode编号避免每次访问文件都从头遍历整个目录。inode cache缓存的是文件元数据包括权限、属主、大小、时间戳、数据块位置等。这两类缓存不占Page Cache而是存放在slab分配器里属于内核对象缓存。方法看slabtopslabtop -o输出里dentry和inode两个条目会非常显眼尤其在高并发访问大量小文件、频繁创建删除文件的场景下这两个数值可能占掉几个GB内存。1.3 三者在一次文件访问里如何配合用一个最常见的操作“打开并读取一个文件”来说明三个缓存是怎么协同的进程发起open()VFS先查dentry cache确认路径对应的目录项是否存在、是否有权限再查inode cache拿到文件的元数据和磁盘块映射。进程发起read()VFS根据inode里的块信息去Page Cache里找对应的数据页。命中了直接返回未命中则从磁盘加载。文件读完后这些页和元数据并不会立刻消失而会继续留在缓存里服务下一次访问。这也解释了开头那个反直觉的现象为什么Linux宁可把空闲内存全拿去做缓存也不愿意让内存闲着。因为内存闲着等于浪费而缓存能在下一次访问时省钱省时。真正的可用内存看free -h里的available字段而不是free字段。available是内核根据可回收缓存实时估算出来的“真实可用内存”它更接近你心里“我还有多少内存能分给新程序”的答案。这里要提醒一个容易踩的细节/proc/meminfo里的Cached并不代表全部都能回收它包含了tmpfs/shmem这类占用的页。tmpfs文件系统本质上是“生活在内存里的文件”这些页虽然不是进程匿名内存但回收它们等于清空临时文件内容代价并不低。所以判断“还有多少缓存能释放”别只看Cached一个字段。2. 缓存占着内存到底要不要清2.1 先理解Linux的内存哲学很多人看到used高就紧张其实Linux的内存管理哲学和Windows完全不同。在Linux眼里空闲物理内存是一种资源浪费与其空着不如拿去缓存文件、加速读写。所以正常负载下的Linux机器内存中绝大部分被缓存占用反而是健康状态。真正需要关注的是内存压力。当系统内存吃紧内核的kswapd内核线程会启动回收或者直接触达分配路径上的直接回收。回收顺序也很有讲究优先回收Page Cache里干净的页无需写回磁盘直接丢弃再回收slab里可回收的dentry/inode对象最后才把匿名页换到swap。这套机制保证了普通进程想要内存时能拿到响应速度最快、损失最小的那部分。所以当free -h显示free很低但available还不错时根本不用慌。真正的危险信号是available持续走低同时swap使用率不断上升这才是内存资源告急的实锤。2.2 三个值得手工调的内核参数虽然内核自动回收很聪明但默认参数并不适配所有场景。下面三个参数是我在服务器上最常调的。参数默认值作用常见调优场景vm.dirty_background_ratio10脏页占可用内存比例达到该值时后台开始异步写回数据库服务器可调低到5减少集中刷盘vm.dirty_ratio20脏页比例达到该值时进程写入会被阻塞强制同步写回写入频繁的机器调低防止写IO瞬时拥堵vm.vfs_cache_pressure100内核回收dentry/inode缓存的倾向性越大越积极文件访问密集的服务可调到50保留更多元数据缓存vm.swappiness60使用swap的倾向性越大越愿意用swap普通服务器可降到10优先保留文件缓存调法很简单临时生效sysctl -w vm.vfs_cache_pressure50永久生效就写进/etc/sysctl.conf然后sysctl -p加载。举个例子。我维护过一台跑高并发图片服务的机器特点是海量小文件访问热点目录下几百个文件被反复打开。默认参数下dentry/inode缓存经常被回收导致访问量一上来磁盘IO立刻飙升。把vm.vfs_cache_pressure调到50后元数据缓存保留得更稳文件打开延迟显著下降。代价是slab内存占用略有上升但换来的是实打实的性能收益。2.3 drop_caches最容易被误解的“清理命令”网上流传最广的“清内存”命令是sync echo 3 /proc/sys/vm/drop_caches先说明它的真实含义echo 1 /proc/sys/vm/drop_caches释放Page Cacheecho 2 /proc/sys/vm/drop_caches释放dentry和inode缓存echo 3 /proc/sys/vm/drop_caches同时释放Page Cache和dentry/inode缓存这个接口的本质是给内核一个调试入口官方文档定位很明确用于内存管理调试不是给生产环境日常清理用的。为什么这么反复强调因为手动清理会带来两个直接后果第一清完Page Cache后原本命中的读请求会直接穿透到磁盘瞬时读IO可能暴涨。我在生产环境见过一台机器执行完drop_caches后数据库的物理读在几分钟内翻了十几倍直接把存储延迟拉满。第二缓存本身不是洪水猛兽应用继续运行缓存会马上重新填满你刚才的清空操作等于白忙一场。什么情况才建议手动清理通常是内存泄漏排查需要干净的基线、或者测试时需要模拟冷缓存场景。如果只是看到used高就顺手echo 3 drop_caches我只能说这颗药治标不治本还可能吃出副作用。真正常做的应该是排查是否有进程内存泄漏、应用配置是不是超出了物理内存承受范围而不是拿内核充当抹布。3. 软件包管理器缓存yum/apt是如何“囤货”的3.1 yum/dnf缓存安装包和元数据都攒在本地yum的缓存默认在/var/cache/yumCentOS 8/RHEL 8之后的dnf则默认在/var/cache/dnf。里面主要存两类东西仓库元数据和rpm包。仓库元数据是每次yum makecache或安装时从远程仓库下载的索引信息包括repomd.xml、primary.xml.gz这些文件。它们体积不大但很重要yum list、yum search依赖这些索引才能快速工作。rpm包则是实际下载的安装包这里有个默认策略区别在CentOS 7上keepcache默认是0rpm包安装成功后会被自动删除只保留元数据如果你希望缓存rpm包必须显式把keepcache1写进/etc/yum.conf。查看当前配置grep keepcache /etc/yum.conf手动把这一项改为keepcache1后每次安装的rpm包都会留在/var/cache/yum/$basearch/$releasever/packages/里。长期积累下来这个目录会有好几个GB甚至十几个GB如果/var分区给得小很快会报警。3.2 apt缓存deb包会默认保留Debian/Ubuntu系的apt和yum走的是另一套逻辑正常情况下apt install下载的.deb包会保留在/var/cache/apt/archives/不会被自动删除。这是设计使然方便用户重装、降级、或者通过dpkg做离线安装。但也意味着只要你不定期清理这个目录会一路胖下去。几个清理命令的区别要记牢apt clean清空/var/cache/apt/archives/下所有deb包彻底释放空间apt autoclean只删除“版本过旧且官方仓库已无法再下载”的deb包保留还能用的apt autoremove清理那些自动安装但现在已无用的依赖包清理的是已安装的软件不是缓存日常维护我建议优先用apt autoclean而不是apt clean前者更温柔既释放了大部分空间又保留最近需要的缓存包。对于yum长期不用的老机器直接yum clean all即可因为rpm包默认本来就不留清理的主要是元数据和可能残留的包缓存。3.3 把缓存变成离线仓库yum/apt的“后悔药”看到热搜里大量“本地yum源搭建”、“CentOS 7本地yum源”这类关键词你会发现一个高频需求场景机房内网、无法访问公网的服务器或者需要在多台机器上部署同一套依赖并且锁定版本。这时候软件包管理器缓存就不是该清除的垃圾而是要精心打包的资产。CentOS/RHEL 7/8搭建本地yum源的经典步骤可以直接照抄准备目录和rpm包mkdir -p /opt/localrepo/Packages cd /opt/localrepo/Packages # 用yumdownloader下载指定包及其依赖需先安装yum-utils yumdownloader --resolve --destdir/opt/localrepo/Packages nginx # 或者从系统镜像的Packages目录拷贝rpm包生成仓库元数据yum install -y createrepo createrepo /opt/localrepo编写本地源配置文件/etc/yum.repos.d/local.repo[localrepo] nameLocal Repository baseurlfile:///opt/localrepo enabled1 gpgcheck0刷新缓存并验证yum clean all yum makecache yum --disablerepo* --enablerepolocalrepo list available这样本地源就上线了。后续其他机器也可以拷贝整个/opt/localrepo目录再配同样的repo文件实现内网批量安装。还有更简单的“ISO即源”方案把系统安装镜像挂载到/mnt直接写baseurlfile:///mnt不需要createrepo。适合只需基础软件包的场景。apt对应离线方案也很成熟# 只下载不安装deb会留在当前目录 apt-get download nginx # 或安装时仅下载不实际安装 apt-get install --download-only nginx拿到的deb包拷到目标机器后用dpkg -i安装依赖关系复杂时再逐层补齐即可。更复杂的依赖解析可以借助apt-offline工具把远程机器上的依赖需求打包再到联网机器上下载最后带回内网安装。本质上离线仓库就是把软件包管理器缓存升级成结构化仓库既解决了联网问题又保证了版本可控是个一鱼两吃的思路。4. 实战排查缓存引发的三类经典问题4.1 /var分区被yum缓存撑爆这是个非常现实的故障。某次客户报障“yum install装到一半报No space left on device”。登录进去一看/var分区使用率100%定位到罪魁祸首是/var/cache/yum一口气攒了7GB多的rpm包。原因是之前有人为了离线安装把keepcache改成了1装完又忘了清理缓存越堆越多。排查命令很简单du -sh /var/cache/yum/* df -h /var处理后空间立刻释放yum clean all顺便还可以清理系统残留的旧内核这也是/boot分区和/var分区的常见占用大头yum install -y yum-utils package-cleanup --oldkernels --count2这个命令保留最近两个内核其余全部清除。操作前确认当前内核版本别把正在用的内核删了。4.2 文件删了磁盘空间却没释放一个更耐人寻味的场景明明rm删了好几个GB的文件df -h一看磁盘还满着。这通常不是缓存问题但很多人会误判成缓存没清干净。最常见原因是“文件被进程占用”。Linux里文件被打开后即使把它从目录结构里删掉只要还有进程持有它的文件描述符这个文件占用的磁盘块就不会释放。排查用lsof | grep deleted输出里能看到很多进程名字后面跟着(deleted)标记的文件。处理办法是重启对应进程或者确认无影响后kill掉进程磁盘空间才会真正归还。还有一个容易被误认为是缓存问题的情况是WSL2。在WSL2的Linux环境下删除大量文件Linux内部df -h确实显示空间释放了但Windows宿主机上那个虚拟磁盘文件vhd/vhdx体积并不会自动缩小。这是因为WSL2把整个Linux文件系统放在一个动态扩展的虚拟磁盘里Linux删除文件只是把虚拟磁盘内部的空间标记为可用宿主机层面的物理文件还是那么大。解决办法是执行wsl --shutdown关停WSL然后在管理员PowerShell里压缩虚拟磁盘Optimize-VHD -Path C:\path\to\ext4.vhdx -Mode Full或者在diskpart里用compact vdisk命令。这一步和linux缓存没有直接关系但从排查路径上看它是“删了文件但空间没释放”这类问题里最容易踩的隐藏关卡。4.3 几条可以长期生效的经验处理过这么多缓存相关的问题下面几条经验我认为可以直接沉淀成规范定期检查/var/cache目录的膨胀情况用crontab加一条du -sh /var/cache/*日志每周看一眼别等磁盘满了才救火。服务器默认关闭swap或调低vm.swappiness不要让内核太早把应用内存换到磁盘优先保住Page Cache的命中率。生产环境禁止无脑执行echo 3 /proc/sys/vm/drop_caches如果非要清先确认这是调试场景并通知业务方做好瞬时IO飙升的心理准备。内网机器提前规划好本地yum/apt源避免每次安装都依赖外网既省时间又省带宽。任何调优参数改动先写入/etc/sysctl.conf做持久化别只是sysctl -w临时生效重启后全部归零等于白调。Linux缓存体系说到底是一套精心设计的内存管理策略。它宁可把空闲内存拿来缓存也不愿让内存闲置。Page Cache、dentry/inode和yum/apt缓存各司其职分别解决的是文件内容、元数据和软件安装的性能与可用性问题。搞明白每一类缓存的角色和边界再按场景去配置和清理系统自然会给你正反馈。最后分享一个我在实操中摸索出来的习惯每个季度固定花十分钟检查一次缓存目录、内核参数和磁盘水位日久天长这台机器的脾气你就完全摸透了等真出问题的时候你心里早就有谱了。
返回列表