du命令于df命令统计不一致问题处理

疑似蜜糖 · · Field Notes

今天早上收到一个报警,测试服务器磁盘使用量100%,然后我着急忙慌的赶快去处理了。

先看具体每个挂载磁盘使用量

df -h

发现/opt 目录挂载磁盘满了。第一反应是估计是cdh里面的组件或者开发的拦截器之类的写log写满了,于是去opt目录下具体看是那个文件夹出了问题

du -h --max-depth=1 /opt/

然后尴尬的一幕出现了,du 命令显示 opt整个目录一共占用 69G

而df 目录提示占用 985G 100%

于是我找了下df与du命令实现原理。。。

du 的工作原理

du 命令会对待统计文件逐个调用fstat这个系统调用,获取文件大小。它的数据是基于文件获取的,所以有很大的灵活性,不一定非要针对一个分区,可以跨越多个分区操作。如果针对的目录中文件很多,du速度就会很慢了。

df 的工作原理

df 命令使用的事statfs这个系统调用,直接读取分区的超级块信息获取分区使用情况。它的数据是基于分区元数据的,所以只能针对整个分区。由于df直接读取超级块,所以运行速度不受文件多少影响。

那么他们最大的区别就是一个是基于文件本身的,一个是基于分区元数据,他们两个统计不一致而去du统计数据小于df 就可以推导出 原因很大可能是某个文件删掉了所以du就不会再统计它了,但是分区元数据没有修改df所以继续做了统计。造成这个现象的大家很容易能联想到的就是某个程序还在占用这个已经被删除的文件的句柄,那么这样这个文件就不会真正的物理删除,所欲元数据还存在。。

一般处理方法

使用lsof命令找到还持有被删除文件句柄的进程,然后关闭进程或者杀掉进程用以释放空间。

lsof | grep deleted

方法一

kill -9 xxxx

方法二

可以使用清空文件的方式来代替删除文件,方式是:echo "" > xxx.log

PS:除了rm外,有些命令会间接的删除文件,如gzip命令完成后会删除原来的文件,为了避免在出现这种问题,前先确认没有进程打开该文件

Replies

北侧工程师

这坑我太熟了,报警盘满冲进去,df 红成一片,du 却一脸无辜。

楼主推理是对的:多半是文件已经删了,进程还攥着 fd,空间吐不出来。我补充两个现场习惯:

  1. lsof | grep deleted(或 lsof +L1,看清楚是谁、哪条路径,再决定杀还是不杀。测试机 kill -9 图省事;线上我更想先确认是不是关键服务、能不能平滑重启。
  2. 能截断就别只 rm。 有的人 echo > filetruncate,进程要是还按旧 fd 写,有时照样怪;更稳的是让进程重新打开日志(重启 / reload / 自己支持 reopen)。gzip 那个提醒也中,压完原文件没了,句柄党一样中招。

还有个少见一点的:挂了 NFS/绑定挂载时,du 统计范围和 df 看的分区也可能对不齐,别只死磕 deleted。不过磁盘瞬间从 69G 跳到 985G 这种量级,还是句柄问题概率最大。

报警别只盯使用率,有余力的话对「deleted 大文件」也做个巡检,能少挨几次凌晨电话。