磁盘满了先别急着删日志——用 df、du 与 lsof 找出真正占空间的文件

举报
yd_226757564 发表于 2026/11/05 11:02:20 2026/11/05
【摘要】 线上服务器最常见的一类“假故障”就是磁盘告警:监控显示根分区使用率超过 90%,人一登录服务器就条件反射地 rm -rf 日志目录,结果空间没回来不说,还可能删掉正在写入的关键数据。正确的做法是三步走:先看哪个分区满了,再找到底是哪个目录在变大,最后确认有没有“已删除但还被进程占着”的文件。下面逐一讲清楚。 第一步:df,看哪个分区满了df 报告的是文件系统层面的空间使用情况,先确认告警到底...

线上服务器最常见的一类“假故障”就是磁盘告警:监控显示根分区使用率超过 90%,人一登录服务器就条件反射地 rm -rf 日志目录,结果空间没回来不说,还可能删掉正在写入的关键数据。正确的做法是三步走:先看哪个分区满了,再找到底是哪个目录在变大,最后确认有没有“已删除但还被进程占着”的文件。下面逐一讲清楚。

第一步:df,看哪个分区满了

df 报告的是文件系统层面的空间使用情况,先确认告警到底落在哪个挂载点上:

$ df -h
Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        7.8G     0  7.8G   0% /dev
tmpfs           7.8G     0  7.8G   0% /dev/shm
tmpfs           7.8G  1.1G  6.7G  15% /run
/dev/vda1        99G   93G  1.2G  99% /
tmpfs           7.8G     0  7.8G   0% /sys/fs/cgroup

几点说明:

  • 只看 Use% 高的那一行。上例中是根分区 / 到了 99%,/run 只是内存盘(tmpfs),不用管。
  • 排查时建议加上 -T 看文件系统类型:df -hT,xfs 与 ext4 在空间回收行为上有差异。
  • 顺手看一眼 inode:df -i。空间明明还有,inode 却耗尽(比如某个目录下堆了几百万个小文件)时,同样写不进文件,表现和磁盘满一模一样,很容易误判。

第二步:du,从大目录往下钻

确认是 / 满了之后,用 du 沿着目录树往下钻。建议按层级来,不要一上来就全盘扫描:

$ du -sh /var/* 2>/dev/null | sort -rh | head
38G     /var/lib
4.2G    /var/log
2.1G    /var/spool
$ du -sh /var/lib/* 2>/dev/null | sort -rh | head
35G     /var/lib/mysql
2.6G    /var/lib/docker

两分钟就定位到 /var/lib/mysql 占了 35G。几个经验:

  • 2>/dev/null 过滤掉权限不足的报错,免得满屏红字干扰判断;
  • sort -rh 按人类可读的大小倒序,head 只看前几名;
  • 定位到目标目录后,用 ls -lhS 按大小排序看具体是哪些文件:ls -lhS /var/lib/mysql | head。

为什么 du 和 df 的数字经常对不上?

这是很多人困惑的地方:du -sh / 显示只用了 60G,df 却说用了 93G。常见原因有三个:

  1. 已删除但仍被进程打开的文件:文件名删了,目录项没了,du 统计不到;但进程还拿着文件句柄,内核不会释放数据块,df 照样计入已用空间。这是最常见的一种,第三步专门处理。
  2. 挂载点遮挡:比如先往 /data 里写了 20G 文件,之后又把一块新盘挂载到 /data 上,那么这 20G 对 du /data 不可见(被挂载盖住了),df 却依然计入原分区。排查时可以用 du -x 限制不跨文件系统统计。
  3. 稀疏文件与块统计口径差异:du 按实际占用的数据块统计,df 按文件系统已分配统计,稀疏文件(sparse file)会让两者出现差异,一般差得不多,不用深究。

记住结论就行:df 算的是文件系统的账,du 算的是目录树的账。两本账对不上时,先怀疑“删了但没释放”和“挂载遮挡”。

第三步:lsof,揪出“删了但还占着”的文件

这是最容易被忽略的一步。很多人删完日志发现 df 的 Use% 纹丝不动,就以为“删错了地方”,其实文件是删掉了,只是空间没释放:

$ lsof +L1
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NLINK NODE NAME
java    18234  app    7w   REG  253,0 32212254720     0 /var/log/app/app.log (deleted)
nginx   19012 root   12w   REG  253,0  8589934592     0 /var/log/nginx/access.log (deleted)

lsof +L1 列出链接数小于 1(即文件名已被删除)但仍被进程打开的文件。上例中:

  • 一个 30G 的 app.log,文件名已经删了,但 Java 进程(PID 18234)还拿着写句柄,30G 空间一分没释放;
  • 一个 8G 的 access.log,被 nginx 进程占着。

处理办法(按推荐顺序):

  1. 重启或重载服务:最干净。systemctl restart app 或 nginx -s reload,进程退出后句柄关闭,空间立即释放。能接受短暂重启的优先选这个。
  2. 截断文件:不能重启的服务,用重定向把文件清空,而不是删除:
    $ : > /proc/18234/fd/7
    
    通过 /proc/<PID>/fd/<FD> 把文件截断为 0,进程的写偏移会被重置,空间立即释放,服务不用停。注意这是清空,不是删除。
  3. 千万不要直接 rm 正在写的文件:删了文件名空间不释放,还丢了排查线索;更糟的是有些程序按 inode 写入,删了重建同名文件后,进程还在往旧 inode 写,新文件永远是空的,日志会“凭空消失”。

小技巧:lsof | grep deleted 也能查,但 +L1 更精准;只查单个进程用 lsof -p <PID>。

预防:别让磁盘“突然”满

救火不如防火,三个习惯:

  1. 日志轮转:给应用日志配 logrotate,按天或按大小切割、压缩、保留若干份。/etc/logrotate.d/app 示例:
    /var/log/app/*.log {
        daily
        rotate 7
        compress
        missingok
        copytruncate
    }
    
    copytruncate 对无法重载日志的程序特别有用——先复制再截断,不用重启服务。
  2. 监控阈值分级:磁盘使用率告警不要只设 90% 一档,建议 75% 预警、85% 告警、95% 紧急,分级处理才有缓冲时间。同时把 inode 使用率也纳入监控。
  3. 定期巡检:每周跑一次 du -sh 看 Top 目录的增长趋势,增长异常的目录提前扩容或迁移;也可以用 ncdu 交互式查看,更直观。

总结

步骤 命令 解决的问题
1 df -h / df -i 哪个分区满了,顺带看 inode
2 du -sh + sort -rh 哪个目录最大,逐层下钻
3 lsof +L1 已删除但被占用的文件,重启或截断释放

下次磁盘告警响起时,先别急着删日志:df 定位分区,du 找到大目录,lsof 揪出“删了但没放”的文件。三步走完,空间回来了,数据也没丢,这才是真正的“清理”。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。