根本原因是df统计文件系统已分配块(含已删未释放文件),du仅统计目录树可见文件;差值即“幽灵占用”空间,主因包括被进程占用的已删除文件、inode耗尽、挂载点干扰、文件系统预留空间等。

遇到文件系统显示已满,但 du 统计总和远小于 df 显示的已用空间,这不是误报,而是典型的空间“幽灵占用”。根本原因在于:df 看的是文件系统超级块里的真实分配块,du 看的是当前目录树下还能被遍历到的文件。两者统计对象不同,差值就是那些“看不见却占着地”的资源。
先确认是不是真的容量满,还是 inode 或其他资源耗尽
执行这两条命令缺一不可:
- df -hT:看各挂载点容量使用率(Use%)和文件系统类型
- df -i:看 inode 使用率(IUse%)
如果 Use% 接近 100%,而 IUse% 很低,说明是磁盘块耗尽;如果 IUse% 到了 100%,哪怕还有几十 GB 空间,也无法创建新文件——这是大量小文件(如日志碎片、临时 session、容器缓存)撑爆了 inode 表。此时删大文件没用,得清空数百万个零散小文件。
排除挂载覆盖和跨文件系统干扰
很多服务器在 /var、/home 下挂了独立分区或 tmpfs(比如 /dev/shm、/run、/tmp)。df 会把每个挂载点单独列出,而 du 默认会递归进入所有子目录,包括那些已挂载的新文件系统,导致重复计算或漏算。
- 用 df -h 查看所有挂载点,留意 /tmp、/var/log/journal、/run 等是否为 tmpfs(内存盘)
- 查 /var 时,加 -x 参数避免跨区:du -sh --max-depth=1 -x /var 2>/dev/null | sort -hr
- 若发现某目录(如 /var/lib/docker)实际是 LVM 卷或 overlay2 存储,需单独进该路径再 du,不能只在 /var 下扫
揪出“已删未释放”的 deleted 文件
这是 df 和 du 差值最大的常见原因:文件被 rm 删除,但进程仍在写它(如 Java 日志、Nginx access.log、Docker 容器 stdout),内核保留数据块和 inode,直到进程关闭 fd。
- 运行 sudo lsof +L1(+L1 表示链接数为 0 的打开文件)
- 或用 sudo lsof | grep deleted 快速筛选
- 输出中类似这样的一行就是线索:java 1234 root 1w REG 8,1 2.1G 123456 /var/log/app.log (deleted)
- 处理方式分情况:
— 允许重启服务:systemctl restart app.service
— 不允许重启:清空该 fd 对应内容,例如进入 /proc/1234/fd/ 找到对应数字(如 1),执行 > 1
检查保留空间与隐藏元数据开销
ext3/ext4 默认为 root 预留 5% 空间(防止系统完全卡死),这部分计入 df 的 Used,但 du 统计不到。可通过以下命令确认:
- tune2fs -l /dev/sda1 | grep "Reserved block count" 查预留块数
- sudo dumpe2fs -h /dev/sda1 2>/dev/null | grep -E "(Block count|Reserved)"
- 若预留比例过高(如 10%),且磁盘确实紧张,可临时调低:tune2fs -m 1 /dev/sda1(设为 1%,慎用)
另外,LVM 快照、XFS 日志、btrfs 子卷配额等高级特性也会造成 df/du 偏差,需结合具体文件系统类型进一步排查。

















