df 显示磁盘满了,du 却找不到大文件:被删除文件仍被进程占用的排查与清理
磁盘告警常常来得很突然:监控说根分区只剩几百兆,应用开始写日志失败,可登录服务器用 du 把各个目录加起来,却怎么也凑不够已用空间。继续删文件,df 显示的可用空间也几乎没有变化。这种“删了也不释放”的现象,有一个很常见的根因:文件已经被删除,但仍被某个进程打开,内核不会真正回收它占用的磁盘块。本文按排查顺序讲清楚 df 与 du 的区别、如何定位这类文件,以及怎样在不误伤业务的前提下释放空间。
一、df 和 du 为什么会对不上
df 看的是文件系统的整体使用情况,从超级块读取已分配块数,反映磁盘上真实被占用的空间。du 沿着目录树逐个统计文件大小,只能看到当前仍存在于目录中的文件。两者口径不同,出现差值并不奇怪,关键是差值有多大、是否持续变大。
文件被 rm 删除时,目录项立即移除,du 再也统计不到它。但如果某个进程已经打开了这个文件,内核会保留它的 inode 和数据块,直到最后一个文件描述符关闭。于是出现经典组合:df 显示已用很高,du 加总却明显偏小,删文件也不见效。
二、先确认现象,再动手
先用同一时间点的命令做对比,避免在业务仍在写盘时被数字误导:
df -h /
du -sh /* 2>/dev/null | sort -h
如果 df 已用率超过 90%,而 du 的合计明显小于 df 的已用值,且差值达到数 GB,就要优先怀疑被删除但未关闭的文件。同时用 mount 确认根分区和数据分区的挂载点,检查有没有目录被后来挂载的文件系统遮住。被遮盖目录里的数据仍占用磁盘,但在挂载点下是看不到的,不要直接按“找不到文件”去删别的东西。
三、用 lsof 定位被删除的文件
lsof 可以列出进程打开的文件,配合 deleted 关键字就能直接看到“已删除但仍占用”的条目:
lsof +L1
lsof | grep deleted
输出里重点看进程名和 PID、文件描述符、文件大小。被删除的大日志文件通常排在前面,大小一栏会显示它仍占用的空间。确认之后,再用 /proc/PID/fd 核对:被删除的文件会显示“(deleted)”后缀,这个目录是只读的观察窗口,适合核对,不适合直接在这里做删除操作。
四、四种最常见的来源
一是应用日志被轮转后旧文件未关闭。日志切割工具把文件改名或删除,但应用没有重新打开日志,旧文件描述符一直持有空间,直到进程重启或收到信号重新打开日志。
二是临时文件被提前删除。程序创建临时文件后立即 unlink,只靠文件描述符读写,正常退出时空间自动回收;程序异常挂起时,描述符不关闭,空间就一直被占着。
三是数据库和中间件的数据文件被误删除。正在运行的数据库持有数据文件描述符,删文件后服务可能还能跑一段时间,但空间不释放,重启后反而可能无法恢复,处理前必须先确认备份和恢复方案。
四是备份、解压和上传的中间文件。任务中断后,子进程可能仍持有已删除的临时文件,父进程退出也不会自动关闭,需要按进程逐个核对。
五、安全释放空间的方法
最稳妥的做法是让持有文件的进程自己关闭描述符。很多服务支持重新打开日志,例如向进程发送约定的信号,或使用日志轮转工具的 reload 动作。操作前先确认该信号在该服务里的含义,不要把重启信号当成重载信号用。
如果确认是日志文件且业务可以接受截断,可以对 /proc/PID/fd 下的描述符做截断,让文件大小归零而不关闭描述符:
: > /proc/<PID>/fd/<FD>
这条命令只适用于已确认是日志、且截断不会破坏数据结构的场景。不要对数据库数据文件、索引文件或正在写入的业务文件这样做。截断后立即用 df 复核,再观察应用日志是否正常续写。
六、验证与预防
释放空间后不要只看一次 df,要连续观察一段时间,确认已用率不再快速回升。同时检查日志轮转配置:轮转后是否通知进程重新打开日志、是否设置保留天数和大小上限、压缩是否及时。把日志目录单独挂载到数据分区,也能避免日志把根分区写满。
结语
df 和 du 对不上,不一定是统计错误,更多时候是文件生命周期和进程持有状态不一致的信号。先用 df 和 du 定量对比,再用 lsof 定位被删除仍被占用的文件,最后按“让进程自己关闭、必要时截断日志、确认安全再重启”的顺序处理,就能在不误伤业务的情况下把空间真正释放出来。
- 点赞
- 收藏
- 关注作者
评论(0)