《100个“反常识”经验02:磁盘满了我du找不到大文件?文件被删了空间却没释放》
先唠两句大家好啊我是阿垚。欢迎来到《100个“反常识”经验》第02期。上期聊了SSH连不上防火墙背了90%的锅。今天聊一个更诡异的问题磁盘满了但du找不到大文件。你是不是也遇到过——df -h显示使用率95%但du -sh *加起来连一半都不到更离谱的是明明把文件删了磁盘空间却一点没变。别急今天带你找到真凶。 一个让我怀疑人生的下午那天监控报警某台生产服务器磁盘使用率飙到96%。我登录上去先执行了常规操作bashdf -h du -sh / 2/dev/null | sort -hr | head -10结果傻眼了df显示用了180Gdu加起来只有90G。那90G去哪了我又去翻了/tmp、/var/log、~/.cache……都没有。折腾了一个多小时最后发现是什么有一个大文件被删了但被某个进程还攥在手里空间根本没释放。这就是Linux里经典的“文件已删除句柄未释放”问题。 为什么会出现这种情况简单解释一下原理当一个进程打开一个文件后即使你在磁盘上把它删了rm只要进程还在运行文件占用的空间就不会释放。因为Linux的文件系统是通过inode引用计数来管理的。rm只是删掉了目录项引用计数减1但如果进程还持有这个文件引用计数不会归零空间就不会回收。所以你会看到文件没了空间也没了。 排错三步法建议收藏第一步确认是不是“幽灵文件”执行以下命令查看被删除但未释放的文件bashlsof | grep deleted或者更精确地只看文件大小bashlsof | grep deleted | awk {print $7, $9} | sort -hr如果看到一大堆(deleted)标记的文件恭喜你找到真凶了。第二步找到罪魁祸首进程从上面输出里找到进程IDPID比如bashjava 12345 root 67r REG 8,1 10737418240 123456 /var/log/app.log (deleted)这里PID是12345是一个Java进程删了一个10GB的日志文件但没释放。确认一下进程在干什么bashls -l /proc/12345/fd/ | grep deleted这会列出该进程所有还攥着的已删除文件。第三步释放空间两种方法方法一重启进程推荐在业务低峰期bashsystemctl restart 服务名 # 或者直接 kill -9 12345 再重新启动重启后进程释放所有文件句柄空间就回来了。方法二清空文件而不是删除无需重启如果进程不能重启可以这样操作bashcat /dev/null /proc/12345/fd/67其中67是上面输出中的文件描述符编号。这条命令会清空那个文件空间立即释放进程继续运行。⚠️ 注意这个方法需要确认清空的文件确实是不需要的别把正在写的重要数据清了。 永久防范方案别等磁盘满了再慌做完以上排查我建议你再做四件事✅日志轮转配置好使用logrotate设置copytruncate选项避免进程锁文件✅监控文件句柄添加告警当lsof | grep deleted累计超过一定阈值时通知✅应用日志规范要求开发使用日志滚动框架如Log4j的RollingFileAppender别自己手写日志逻辑✅定期巡检脚本每周执行一次检查是否有“幽灵文件”bash#!/bin/bash count$(lsof 2/dev/null | grep -c deleted) if [ $count -gt 0 ]; then echo 发现 $count 个未释放的文件句柄 lsof 2/dev/null | grep deleted | head -10 fi 下期预告《反常识03服务器时间跳变导致K8s证书失效——凌晨3点的惊魂30分钟》评论区聊聊你遇到过最诡异的磁盘问题。——阿垚一个踩过2万次坑的“老”IT