大概上周五快下班的时候,监控告警弹了一串:某台测试服务器 / 分区使用率 96%。这类问题我处理过太多次,登录上去先用df -h看了一眼,然后就开始常规操作——清 apt 缓存、删 /var/log 里旋转出来的老日志、再跑一遍docker system prune。干完这波,分区使用率掉到了 71%。今天这篇就专门聊聊 Linux 下清理无用缓存和垃圾文件这件事,既包括你怎么安全地找到它们,也包括哪些“缓存”其实动不得。适合刚接触 Linux 服务器维护的读者,也适合被磁盘告警折腾过几次的运维老哥参考。
1. 动手之前,先搞清楚Linux的缓存到底是个啥
1.1 内核页缓存:删了只会更慢
很多新手看到free -h里的 buff/cache 有好几 GB,就觉得自己系统被缓存占满了,想方设法去释放。这里必须说清楚:这些 page cache 是内核拿空闲内存来缓存磁盘读写内容的,目的是减少真实磁盘 I/O,让程序跑得更快。你通过echo 3 > /proc/sys/vm/drop_caches可以把它们清掉,但清完之后系统并没有变快,反而下次读文件时还要重新从磁盘加载,纯粹是自己给自己找麻烦。
所以做清理之前,你先要建立一个大前提:清理的目标是“确定性垃圾”,而不是内核自动管理的缓存。所谓确定性垃圾,就是软件运行时写下来、但已经失去价值、不会被重复利用的文件,比如包管理器下载的过时安装包、旋转了 N 轮的日志、构建镜像时留下的中间层,这些才是你需要处理的对象。内核页缓存是动态回收的,内存不够时会自动释放,不需要你手动干涉。
1.2 需要清理的“垃圾文件”到底藏在哪
Linux 把不同类型的文件分散在固定目录里,这也是它比 Windows 更“规整”的地方。最常见的垃圾文件来源包括:包管理器缓存(/var/cache/apt、/var/cache/yum、/var/cache/dnf)、日志文件(/var/log 下的 .gz、.1、.old 等)、临时文件(/tmp、/var/tmp)、用户级缓存(~/.cache、~/.npm、~/.cache/pip),以及 Docker 镜像遗留层和构建缓存。这些路径基本都允许安全删除,只是删除时有各自的注意事项。
我建议你用表格把这些类型和路径记下来,后续排查会快很多:
| 类型 | 常见路径 | 风险等级 | 典型命令 |
|---|---|---|---|
| 包管理器缓存 | /var/cache/apt/archives | 低 | apt clean |
| 旧内核 | /boot | 中 | apt autoremove --purge |
| 系统日志 | /var/log、/var/log/journal | 低 | journalctl --vacuum-time=7d |
| 临时文件 | /tmp、/var/tmp | 低 | find /tmp -mtime +7 -delete |
| 用户缓存 | ~/.cache | 低 | rm -rf ~/.cache/* |
| Docker 垃圾 | /var/lib/docker | 中 | docker system prune |
这里“低风险”不等于随便删。比如旧内核你删错了,重启可能进不了系统;Docker 里有些自定义镜像如果没打 tag 关联,system prune -a全部清掉之后要用还得重新构建。道理就是:每一个操作前先想清楚“这个文件还有没有可能被拿来使用”。
2. 三步定位磁盘空间:哪些目录最吃容量
2.1 用 df 和 du 快速扫出大文件
很多场景下,你不知道空间到底被什么东西吃掉了,直接盲删大概率会误伤。我的习惯是三步走:先df -h看整体挂载情况,再du -sh /var/* /usr/* /home/* 2>/dev/null | sort -h定位大目录,最后用du -sh /var/log/* 2>/dev/null | sort -h这类命令往子目录继续钻。遇到目录特别大但文件隐藏得很深时,推荐装个ncdu,用它交互式浏览哪个子目录占了多少空间,比一串命令挨个跑要直观得多。
有一次我碰到/被占满,du扫了一圈才发现是 journald 日志累积了 20 多 GB。这些日志如果没有设置上限,会一直涨到吃掉大部分磁盘。类似的还有/var/lib/docker,容器的 overlay2 层会存很多中间状态,时间久了几个 G 很正常。所以排查的时候,优先看 /var 和 /home 下有没有异常大文件,基本能覆盖 80% 的情况。
2.2 被忽略的“空间黑洞”目录清单
除了日志和 Docker,还有几个容易忽略的地方:/root/.cache、/home/某用户/.local/share/Trash(回收站)、/var/cache/man里的手册缓存、/usr/src下的内核源码,以及snap的旧版本文件。Snap 有个别的包管理器没有的习惯:为了支持版本回滚,会把旧版本完整保留,导致/var/lib/snapd/snaps越来越大。你可以在终端跑snap list --all,把里面标记为 disabled 的旧版本用snap remove 包名 --revision=旧版本号清掉。这个点很多人不知道,清理后效果很明显。
磁盘空间排查是个体力活,但方向对了效率就高。我的建议是不要在根目录一个一个找大文件,要有针对性地先扫 /var、/home、/opt 这几个变量最多的路径。如果你用 LVM 或裸设备做数据盘,也要注意把数据安装在 /data 这类独立分区,别全堆在系统分区里,否则一告警就波及系统服务。
3. 实际操作:安全清理Linux缓存与垃圾文件
3.1 包管理器缓存的清理(apt/yum/dnf)
Debian/Ubuntu 系的包管理会长期保留已经下载过的 .deb 文件。它们本身是安全的,但在你已经不打算重装这些包时,它们就成了纯浪费磁盘的垃圾。跑一句sudo apt clean可以清空/var/cache/apt/archives;sudo apt autoclean只删除没法再下载的旧版本;sudo apt autoremove --purge则会把系统里已经不再依赖的旧包,连同旧内核一起处理掉。Red Hat 系对应命令是yum clean all和dnf clean all。清理前后建议都跑一下du -sh /var/cache/apt,你会看到变化。
另外,很多开发机还会用 pip 和 npm 装包。~/.cache/pip和~/.npm这两个缓存目录往往能占掉好几个 G,而且删除它们对现有环境没有任何影响。如果你做 Python 开发,pip cache purge是更规范的做法;npm 则可以直接删~/.npm/_cacache。实测下来,连着一个多月频繁构建 Python 项目的机器,光 pip 缓存就能攒到 3-4G,清完之后的释放量相当可观。
3.2 日志文件的有效清理与 journald 配置
日志是系统里最容易“默默膨胀”的东西。传统的 syslog 会按日期或大小滚动,一般问题不大;但 journald 的/var/log/journal默认没有空间上限,如果长时间没人管,几十个 G 非常常见。推荐的做法是先用journalctl --disk-usage看一下当前占用,然后用journalctl --vacuum-size=200M把日志压缩到 200MB 以内,或者journalctl --vacuum-time=7d只保留最近 7 天的日志。
这还没完,因为重启后 journald 还会接着涨。你要去/etc/systemd/journald.conf里把SystemMaxUse改成你期望的值,比如 500M,然后systemctl restart systemd-journald生效。从我的经验看,这个配置在很多新装的系统里都没人动过,导致磁盘空间被日志占掉 30% 以上的情况时有发生。清理日志之前,记得先看下有没有应用依赖旧日志做审计,如果有业务合规要求,就按保留策略来,别一刀切删光。
3.3 临时文件、用户缓存和 Docker 清理
/tmp下的文件是临时用的,但系统默认不会自动清理掉长期不动的文件,顶多按某种策略清极老的文件。手动清理时注意别把正在运行的进程持有的 socket 和临时文件删了。建议用 find 命令只删 mtime 超过 7 天的文件:sudo find /tmp -type f -mtime +7 -delete,目录可以用-type d -empty -delete删空目录。至于/var/tmp,里面的文件不会因为重启就消失,同样可以按时间清理。
用户级缓存更简单,每个用户的~/.cache目录可以直接往里挖。我用du -sh ~/.cache/* | sort -h找到最大的几个子目录后,只针对性删除不常用的(比如浏览器缓存、缩略图、旧下载残留)。不要直接rm -rf ~/.cache,因为有些应用会把配置写在里面,删了重启时会重新初始化,搞不好让你重登一遍。
Docker 是另一个大垃圾来源。docker system prune可以清理已停止的容器、悬空镜像和无用网络;docker system prune -a则会进一步把没在用的镜像全删掉。构建镜像时留下的中间缓存用docker builder prune清理。我碰到过一台构建机,/var/lib/docker/overlay2占了 80 多 G,跑完docker system prune -a加上docker builder prune -f,直接降到 20G。如果你用 Docker 只是偶尔跑个测试镜像,这种清理的收益非常明显。
4. 清理后空间没释放?常见坑位逐个排查
4.1 文件被进程占用:为什么删了文件空间反而更满
有时候你删完一个大文件,df -h一看空间还是没回来,这是因为文件虽然被删了,但某个进程依然持有它的文件句柄,操作系统不会真正释放磁盘块,直到进程退出或关闭句柄。用lsof +L1可以列出所有处于这种状态的文件,找到 PID 后,你可以考虑重启对应进程,或者直接kill它(前提是你确认它的作用)。我记得处理过一个场景,旧日志被直接删了,但旧进程一直占着句柄,导致空间明明“删”了却始终不释放,最后重启了服务才恢复正常。
所以我在清理日志和大文件时会顺手跑一句lsof | grep deleted,如果看到大量残留,说明你的应用在使用文件的方式上可能有问题,比如日志框架没有正确 reopen 文件。这个锅不要甩给“删不掉”,问题是进程活着。
4.2 WSL 虚拟磁盘删除文件后不回收
Windows 上用 WSL 的朋友应该都有这个经验:在 WSL 里rm掉几个 G 文件,Windows 端 C 盘空间完全没变化。这是因为 WSL 的 ext4 文件系统被封装在一个 vhdx 虚拟磁盘里,删除文件是释放了 ext4 里的块,但 vhdx 文件本身不会自动瘦身。处理办法是先关掉 WSL:wsl --shutdown,然后以管理员身份打开 PowerShell,运行Optimize-VHD -Path <你的ext4.vhdx路径> -Mode Full,或者用 diskpart 的compact vdisk。跑完之后 C 盘空间才会真正降下来。
如果你用的是 WSL2,vhdx 路径一般在%LOCALAPPDATA%\Packages\...\LocalState\ext4.vhdx。这个操作偶尔在大量删除后做一次就行,不用每次删文件都压缩。还可以考虑把 WSL 发行版迁移到非系统盘,避免系统盘被虚拟磁盘占满。
4.3 journald 清理后没变化,可能是别家日志在作怪
有时候执行完journalctl --vacuum-size,空间却没有明显下降,让你怀疑命令没生效。其实 journald 压缩需要一点时间,而且它不会处理非 journald 管理的日志,比如/var/log/syslog、/var/log/messages或者某个应用的.log文件。这种情况要用du -sh /var/log/*逐个看,重点检查有没有超过 1G 的单文件。别忘了一些应用会无限写日志,比如 Tomcat 的 catalina.out、Nginx 的 access.log,定期用logrotate配置轮转是更根本的解法。
排查思路就是:先看du再决定删什么,不要只盯着工具的执行结果。日志清理没有一劳永逸,关键是建立轮转策略。
4.4 误删缓存后的紧急处理
误删一般发生在你执行了一个含糊的 rm 命令,比如sudo rm -rf /var/cache之类的。绝大多数包管理器缓存被删后会自动重建,影响不大;真正危险的是误删了/etc、/usr或数据库数据目录。这时候先别慌,第一步是马上停掉相关服务,防止继续写入覆盖未分配空间;第二步看有没有快照或备份,如果有,直接恢复;如果没有,就只能靠备份策略和运气了。我在生产环境的原则是:能用包管理器命令清理的就用命令清理,能加--dry-run的先跑一遍模拟,绝不为了省事写一个覆盖全部缓存路径的 rm -rf。
5. 把清理做成脚本:常用命令与避坑清单
5.1 我的日常维护脚本骨架
这些清理动作如果手动重复,很容易在某一步漏掉。我习惯写一个只包含安全命令的脚本,放在 cron 里定期执行。下面是精简版:
#!/bin/bash # 安全清理脚本:只处理确定性垃圾 set -euo pipefail echo "=== 清理 apt 缓存 ===" sudo apt clean sudo apt autoclean || true echo "=== 清理 journald 日志(保留200M)===" journalctl --vacuum-size=200M echo "=== 清理 pip 与 npm 缓存 ===" pip cache purge || true rm -rf ~/.npm/_cacache || true echo "=== 清理 /tmp 下7天前的文件 ===" find /tmp -type f -mtime +7 -delete 2>/dev/null || true find /tmp -type d -empty -delete 2>/dev/null || true echo "=== Docker 可选清理 ===" # docker system prune -af这个脚本有几个设计点:每步都带|| true防止某条命令失败导致脚本中断;只删有确定收益且风险低的缓存;Docker 清理默认注释掉,需要时手动解开,避免把正在使用的镜像误删。脚本跑完可以用df -h对比前后变化。
5.2 这些危险操作千万别试
最后划几条红线。第一,不要为了释放内存去echo 3 > /proc/sys/vm/drop_caches,这会造成性能抖动。第二,不要清理/dev/shm,那里是共享内存,很多程序(包括数据库)正在使用。第三,不要轻易清空/usr/lib下的动态库缓存,比如ldconfig管理的文件,删了会导致一堆程序启动失败。第四,不要在业务高峰期执行大批量 Docker 清理,可能影响正在构建或拉取镜像的流程。第五,任何rm -rf /*之类的操作绝对禁止,这种命令手滑一下系统直接没了,别觉得自己不会打错。
我在实际维护中给自己定的规矩就三条:能不用 rm 就不用 rm,能用官方命令清理就不用自定义命令,删之前永远先du和df对比前后数据。这套流程我用了很久,没翻过车。你下次再遇到磁盘告警,照着这个思路走一遍,应该能把空间找回来。