明明删了几个 G 的日志,df -h 一看磁盘还是 100%?用 du -sh 检查目录,却发现实际占用并不高……
别慌。这种现象在运维中很常见 —— 文件已经删了,但磁盘空间没释放。今天我们就从原因、排查到解决,把这事彻底讲明白,顺便帮你弄懂 df 和 du 为什么总“打架”。
一、现象重现:删了文件,空间却没回来
假设你遇到下面这种情况:
# 查看磁盘使用率,/var 分区已经 100%$ df -h /varFilesystem Size Used Avail Use% Mounted on/dev/sda3 20G20G0100% /var# 查看 /var/log 目录实际大小,只有 2G$ du -sh /var/log2.0G /var/log# 刚才明明删了一个 15G 的日志文件 app.log$ rm -f /var/log/app.log
矛盾出现了:
答案:有一个进程还在“抱着”那个已经删掉的文件不放手。
二、为什么会出现这种情况?
1. Linux 删除文件的真实过程
在 Linux 中,一个文件由两部分组成:
执行 rm 时,只是从目录中删除了这个映射。如果此时没有任何进程打开这个文件,内核才会真正回收数据块。但如果有进程正在使用这个文件(比如一直在写日志),即使目录项没了,进程仍然保留着文件的“通行证”(文件句柄)。于是:
这些“无名”的文件,很多运维朋友叫它 幽灵文件。
2. 哪些场景容易踩坑?
- 应用直接写日志(比如 Java、Tomcat、Nginx),你直接
rm 删了日志文件
三、df 与 du 的“矛盾”本质
1. 它们到底怎么统计的?
2. 为什么结果不一样?
df 直接读文件系统元数据 → 分区用了多少块,它就报多少(包括幽灵文件)du 只能看到当前目录里“还存在”的文件 → 看不到已从目录删除的文件
所以当幽灵文件出现时:
df 看到的:分区占用 = 正常文件 + 幽灵文件du 看到的:分区占用 = 正常文件(幽灵文件不可见)
结果就是 df 显示使用量明显大于 du。
顺便说一句,即使没有幽灵文件,df 和 du 也可能有少量差异(比如文件系统预留空间、元数据开销),但正常差异一般不超过 5%。如果差了好几个 G,基本就是幽灵文件没跑了。
四、如何定位“幽灵文件”?
1. 快速检查
执行下面这条命令:
输出类似这样:
java 1234root5wREG8,315360000123456/var/log/app.log (deleted)
信息解读:
/var/log/app.log (deleted):原文件路径,括号里的 deleted 说明它已被删除但未释放
如果屏幕上刷出一堆这样的记录,那元凶就找到了。
2. 更精确的过滤(只看真正占空间的)
+L1 会筛选出链接数为 0 但仍被打开的文件,命中率更高。
五、如何释放空间?(3 种方法)
✅ 方法一:杀掉占用进程(最直接)
优点:空间瞬间释放缺点:会中断业务,需要评估影响
比如上面查到 PID=1234,执行 kill -9 1234 后,磁盘空间就回来了。
✅ 方法二:清空文件内容(生产环境推荐)
如果你不想杀进程,可以清空文件内容,而不删除文件本身。
先找到文件描述符(FD):
ls -l /proc/1234/fd/ | grep deleted
输出示例:
lrwx------ 1 root root 64 ... 5 -> /var/log/app.log (deleted)
其中 5 就是文件描述符编号。然后清空它:
或者用 truncate:
truncate -s 0 /proc/1234/fd/5
效果:文件内容被清空,进程继续运行,磁盘空间立刻释放。很适合不能重启服务的场景(比如核心数据库、在线业务)。
✅ 方法三:重启服务或服务器
- 重启持有该文件句柄的服务(如
systemctl restart nginx) - 最后手段:重启整台服务器(慎重,适合测试环境或实在没招的时候)
六、怎么预防?别让问题再发生
| |
|---|
不要直接 rm 正在被写的文件 | 改用 echo "" > file 或 truncate -s 0 file 清空 |
| 使用 logrotate | 配置 copytruncate 选项,先拷贝再清空,进程不中断 |
| 定期监控 | 写个 crontab,每天执行 lsof +L1 | grep deleted,有异常就告警 |
| 养成好习惯 | |
七、总结
- 现象:
rm 删了文件,df 显示空间没释放,du 显示已释放 → 大概率是幽灵文件 - 解决:
kill 进程 或 清空 /proc/PID/fd/FD - 预防:不直接
rm 正在写的文件,用 logrotate + copytruncate
记住一句话:
在 Linux 世界里,文件是否真的“消失”,不光看目录,还要看有没有进程还“抱着”它。