df 与 du 对不上:文件删了空间没释放的定位与处置
现象
执行 rm 删掉一个大日志或大镜像后,df -h 的已用空间纹丝不动,而 du -sh 统计的目录总和不增反减,两者差出几十 GB,重启服务甚至重启机器才恢复。这类问题几乎都不是 df 统计出错,而是文件已经被删,但仍有进程打开着它。
原理:unlink 只摘掉目录项
Linux 删除文件实际是 unlink():把文件名从目录项里摘掉,并让 inode 的链接计数减一。数据块只有在链接计数为 0、且没有任何进程持有该文件的打开描述符时才真正释放。如果进程还持有 fd(尤其是长期运行、通过重定向写日志的进程),文件名消失了、inode 和数据块却仍被引用,df 自然看不到释放。
定位:先分清是被占用还是统计口径不同
- 确认同一文件系统:df -h /var/log 与 du -xsh /var/log,-x 保证不跨挂载点。
- 查已删除但仍被打开的文件:lsof +L1 列出链接数为 0 的打开文件,lsof +L1 /var 可限定目录;没有 lsof 时直接看 ls -l /proc/*/fd 并过滤 deleted。
- 确认占用进程与大小:lsof +L1 按 SIZE/OFF 排序取头部,那就是仍被占住的空间;再配合 ls -l /proc/<pid>/fd 找到具体文件。
处置:不要用重启凑合
三种做法,按风险从低到高:
- 日志类文件:truncate -s 0 /proc/<pid>/fd/<n>,进程继续往同一个 fd 写,只是从 0 开始,不用重启。
- 非日志的大文件(如已删的临时镜像):只能让持有进程关闭该 fd,通常是重启对应服务;若进程支持 reload,优先 reload。
- 确认可以接受数据丢失时,也可对 /proc 路径 truncate 后观察 du 与 df 回归一致。
注意 lsof 显示的路径带 (deleted) 后缀,此时用原路径 truncate 会报 No such file,必须走 /proc/<pid>/fd/<n>。
预防
- logrotate 的 copytruncate 与 create 两种策略要想清楚:copytruncate 不改变 inode、不要求进程重开文件,但复制窗口内可能丢日志;create 加 postrotate 重载进程才真正换 inode。
- 应用侧优先用按大小或时间滚动的日志库,避免长期持有同一个 fd 写单个文件。
- 监控里同时采集 df 使用率与 du 总和,两者持续背离就该查 deleted 文件。
还有几种正常的差异
排除 deleted 文件后,df 与 du 不一致还有几类常见原因:挂载点覆盖(目录里原有文件被新挂载遮住,du 数得到、df 不计入)、硬链接被 du 重复计算、稀疏文件与预分配文件(du 按块、df 按实际占用)、ext4 默认给 root 预留 5% 空间、NFS 客户端缓存延迟。判断顺序建议固定为:先 lsof +L1,再看挂载与硬链接,最后才怀疑文件系统本身。
小结
df 与 du 对不上,第一反应应该是有没有进程还开着已删文件,而不是统计坏了。一条 lsof +L1 通常就能定位,处置优先 truncate 而非重启,预防则落在日志滚动策略上。
- 点赞
- 收藏
- 关注作者
评论(0)