1. 从一次告警说起:Non DFS Used 到底是什么
凌晨两点,监控面板上那个红色的磁盘使用率数字跳到了 92%,而 HDFS 自身的dfsadmin report却显示 DataNode 的 DFS Used 只有 58%。中间这三十多个百分点的差值,就是今天要聊的主角——Non DFS Used。很多刚接触分布式存储运维的朋友第一次看到这个指标都会懵:明明 HDFS 里没存那么多数据,磁盘怎么就快满了?更让人头疼的是,这个空间不像普通文件那样能直接du出来,删起来也没那么直观。
先把概念说清楚。在 HDFS 的 DataNode 视角里,一块数据盘的空间被划分成几块:真正被 HDFS 块文件占用的叫DFS Used,预留出来给副本、快照、回收站等用的叫DFS Remaining,而既不属于 HDFS 块文件、又实实在在占着磁盘的那部分,就是Non DFS Used。它的计算公式在源码里写得很直白:
Non DFS Used = Configured Capacity - DFS Used - DFS Remaining注意这里的Configured Capacity是 DataNode 配置的数据目录所在磁盘的总容量,而不是整块物理盘。所以 Non DFS Used 本质上是一个"兜底差值"——凡是 DataNode 算不明白、但又确实被占用的空间,都会被归到它头上。这就解释了为什么它经常是个"背锅侠":日志文件、临时文件、被删除但句柄未释放的文件、其他进程写的数据、甚至文件系统本身的元数据,都可能被算进去。
我见过太多人一看到 Non DFS Used 高,第一反应就是去 HDFS 里删数据,结果删了半天指标纹丝不动。原因很简单,这部分空间压根不在 HDFS 的管理范围内。要真正解决问题,得先搞清楚它到底被谁吃了。下面这张表是我在实际排查中总结的常见来源,可以先对照着定位方向:
| 来源类型 | 典型表现 | 排查手段 |
|---|---|---|
| 系统日志 | /var/log持续增长 | du -sh /var/log/* |
| 临时文件 | /tmp、/var/tmp堆积 | ls -lht /tmp |
| 已删除未释放 | lsof能看到 deleted 标记 | lsof | grep deleted |
| 其他服务数据 | 同盘部署了别的组件 | du -sh /*逐层排查 |
| 文件系统保留块 | ext4 默认保留 5% | tune2fs -l查看 |
| 快照/备份残留 | 快照目录占用 | 查看快照策略 |
理解了这个指标的"兜底"性质,后面的排查思路就顺了:既然它是差值,那我们要做的就是把这个差值一点点拆开,找到真正占用空间的那个"元凶"。这也是为什么我常说,Non DFS Used 清理不是单纯的删文件,而是一次完整的磁盘占用溯源。
2. 别急着删数据:先建立正确的排查顺序
很多人处理这类问题的习惯很不好——上来就rm -rf,删完发现没效果,再删,最后把有用的东西也删了。我在早期也犯过这个错,有一次直接把一个还在被进程写入的日志文件删了,结果空间没释放,服务还因为找不到文件句柄报了一堆错。所以这里必须先讲清楚排查顺序,顺序错了,后面全是白费功夫。
2.1 第一步永远是确认指标口径
在动手之前,先用 DataNode 的 JMX 或者hdfs dfsadmin -report把几个关键数字拉出来对齐。重点看三个值:Configured Capacity、DFS Used、Non DFS Used。有时候你会发现 Non DFS Used 是负数,这通常意味着 DataNode 的容量配置和实际磁盘对不上,比如配置里写的是 500G,实际盘只有 400G,或者多个数据目录跨了不同大小的盘。这种情况不是空间被占用,而是配置错误,得先去改dfs.datanode.data.dir的配置。
还有一种情况是刚扩容完新盘,DataNode 还没完成块扫描,指标会短暂失真。这时候别急着操作,等一轮block report跑完再看。我一般会观察 10 到 15 分钟,确认数字稳定了再进入下一步。
2.2 第二步用 df 和 du 做交叉验证
df -h看的是文件系统层面的真实占用,du -sh看的是目录累加的大小。这两个数字经常对不上,而对不上的那部分,往往就是 Non DFS Used 的重要线索。举个我实际遇到的例子:df显示某块盘用了 80%,但du -sh /data加起来只有 50%。中间 30% 去哪了?后来用lsof | grep deleted一查,发现是一个被删除但进程还在写的日志文件,占了将近 200G。
这里有个经验:du 统计的是目录树里当前存在的文件,df 统计的是文件系统实际分配的块。所以当两者差异巨大时,优先怀疑"已删除未释放"和"稀疏文件"这两类情况。稀疏文件比较隐蔽,du默认按实际占用算,df按逻辑大小算,用du --apparent-size才能看出来。
2.3 第三步才是逐层定位大目录
确认了差异存在,再开始逐层往下钻。我的习惯是从根目录开始,用du -h --max-depth=1 /一层层缩小范围。注意不要一上来就对整个盘做du,数据量大时能跑几个小时,还拖慢 IO。可以先用du -h --max-depth=1快速定位到某个大目录,再针对那个目录深入。
排查过程中有个小技巧:用ncdu这个工具会比du直观很多,它能交互式地展示目录占用,还能直接在里面删文件。不过生产环境不一定装得了,所以du的组合命令还是得熟练。下面是我常用的一套命令,可以直接抄:
# 查看各挂载点使用率 df -h # 查看根目录下各一级目录占用 du -h --max-depth=1 / 2>/dev/null | sort -rh | head -20 # 查找已删除但未释放的文件 lsof | grep deleted | awk '{print $7, $2, $1, $9}' | sort -rn | head -20 # 查看文件系统保留块比例 tune2fs -l /dev/sdX | grep -i reserved注意:
lsof | grep deleted在文件句柄特别多的机器上会很慢,建议配合timeout使用,避免卡死终端。
把这三步走完,基本就能锁定 Non DFS Used 的大致来源了。接下来才是针对性地清理,而不是盲目删数据。
3. 逐类击破:Non DFS Used 的六大常见来源与处理
定位到方向之后,就要按类型来处理了。不同类型的占用,清理方式和风险等级完全不同。我按处理难度和常见程度排了个序,从最容易处理的开始讲。
3.1 系统日志与临时文件:最容易被忽视的"温水煮青蛙"
系统日志是 Non DFS Used 里最典型的"慢性增长"来源。/var/log下的日志如果不做轮转,几个月就能吃掉几十上百 G。尤其是那些开了 debug 级别的服务,日志增长速度非常夸张。我见过一台机器因为某个组件日志级别配错,三天写了 400G 日志,直接把盘撑满。
处理这类问题,核心不是删,而是配置轮转。logrotate是标准方案,但很多人配了之后没验证,结果轮转规则写错了,日志还是无限增长。我的建议是配完之后手动跑一次logrotate -f /etc/logrotate.d/xxx,确认切割和压缩都正常。
临时文件方面,/tmp和/var/tmp是两个重灾区。有些程序退出时不清理自己的临时文件,日积月累就堆起来了。可以写个定时任务,定期清理超过 7 天没被访问的临时文件:
find /tmp -type f -atime +7 -delete find /var/tmp -type f -atime +7 -delete但要注意,有些服务的 socket 文件或者锁文件放在/tmp里,删了会导致服务异常。所以清理前最好先ls看一眼,确认没有正在使用的文件。我一般会先跑find不带-delete,把要删的列出来人工过一遍,确认没问题再加删除动作。
3.2 已删除未释放的文件:空间"假释放"的元凶
这是最让人抓狂的一类。文件明明删了,du也看不到,但df显示空间没回来。原因是有进程还持有这个文件的句柄,Linux 的文件系统只有在所有句柄都关闭后才会真正释放块。这种情况在日志文件上尤其常见——运维用rm删了日志,但写日志的进程没重启,句柄还在,空间就一直占着。
排查方法前面提过,用lsof | grep deleted。找到之后有两种处理方式:一是重启持有句柄的进程,让它释放;二是如果不想重启,可以用> /proc/PID/fd/FD的方式清空文件内容,这样句柄还在,但空间能释放出来。第二种方式更温和,适合不能随便重启的核心服务。
# 找到占用大文件的进程和文件描述符 lsof | grep deleted | sort -k7 -rn | head # 清空该文件描述符对应的内容(谨慎操作) : > /proc/12345/fd/67注意:第二种方式本质上是把文件截断为 0,如果进程还在往里写,会从 0 开始继续写。对于日志类文件没问题,但对于正在被读取的数据文件,可能导致数据损坏,操作前务必确认文件类型。
我个人的经验是,与其事后清理,不如事前规范。日志文件一律用logrotate管理,不要手动rm;需要清理时用truncate -s 0而不是rm,这样句柄不会失效,空间也能立即释放。
3.3 同盘部署的其他服务:最容易被忽略的"邻居"
很多集群为了省机器,会把 DataNode 和其他服务部署在同一块盘上。比如把 Kafka 的日志目录、Elasticsearch 的数据目录、或者某个数据库的数据文件放在 DataNode 的数据盘上。这些服务的数据对 HDFS 来说就是 Non DFS Used,而且它们往往增长很快。
这种情况的排查方法是看 DataNode 数据目录的挂载点,然后du这个挂载点的根目录,看看除了 DataNode 的数据目录之外,还有哪些目录在占空间。我遇到过一次,某台机器的 Non DFS Used 高达 40%,最后发现是同盘的 Kafka 日志没配清理策略,堆积了 2T 多。
处理方式有两种:短期是给这些服务配置清理策略或者迁移数据;长期是做好磁盘规划,DataNode 的数据盘尽量专用,不要和其他 IO 密集型服务混部。如果实在要混部,至少给每个服务设置磁盘配额,避免某一个服务把盘吃满影响 HDFS。
3.4 文件系统保留块:ext4 默认吃掉 5%
这个点很多人不知道。ext4 文件系统默认会给 root 保留 5% 的空间,用于防止普通用户把盘写满导致系统无法运行。对于一块 4T 的盘,5% 就是 200G。这部分空间df会算进 Used,但du看不到,所以也会被归到 Non DFS Used 里。
查看保留块比例:
tune2fs -l /dev/sdX | grep -i "reserved block count"如果是专门给 DataNode 用的数据盘,可以把保留比例调低,比如调到 1%:
tune2fs -m 1 /dev/sdX这样能释放出 4% 的空间。不过要注意,调低保留块后,如果盘真的写满了,root 用户也可能无法写入,影响系统稳定性。所以这个操作适合那些有独立监控、能及时告警的数据盘,不建议在系统盘上做。
3.5 快照与备份残留:看不见的"时间胶囊"
如果 DataNode 的数据目录所在文件系统开了快照功能(比如 LVM 快照、ZFS 快照、或者某些商业存储的快照),那么快照会占用额外的空间。这些空间对 HDFS 不可见,但实实在在占着盘。快照的特点是"写时复制",原数据被修改时,旧数据块会被保留在快照里,所以快照越多、保留时间越长,占用越大。
排查方法是看存储层面有没有快照策略,以及快照的实际占用。不同存储的查看方式不一样,LVM 用lvs,ZFS 用zfs list -t snapshot。处理方式就是清理过期快照,或者调整快照保留策略。这个操作一般在存储层面做,需要和存储管理员配合。
3.6 稀疏文件与预分配文件:逻辑大小和实际占用的差异
有些应用会创建稀疏文件或者预分配文件,比如数据库的数据文件、虚拟机的磁盘镜像。这类文件的特点是逻辑大小很大,但实际占用的块可能很少。du默认显示实际占用,df显示的是文件系统分配的块,两者会有差异。如果应用预分配了空间但没实际写入,df会显示已占用,但du看不到。
排查这类文件可以用:
# 查找实际占用远小于逻辑大小的文件 find /data -type f -size +1G -exec du -h {} \; | sort -rh | head对比ls -lh和du -h的结果,如果差异很大,就是稀疏文件。处理方式取决于应用,一般不建议直接删,而是联系应用负责人确认是否可以清理或迁移。
4. 一次完整的清理实战:从 92% 降到 65%
光讲理论不够,我把最近一次实际处理的完整过程复盘一下。这次的情况比较典型,涵盖了上面说的好几类来源,可以当作一个综合案例来看。
4.1 现场情况与初步判断
某天下午收到告警,一批 DataNode 的磁盘使用率超过 90%,但 HDFS 的 DFS Used 只有 55% 左右。登录其中一台机器,先跑df -h和hdfs dfsadmin -report对齐数据:
Filesystem Size Used Avail Use% Mounted on /dev/sdb1 3.6T 3.3T 120G 97% /data1DataNode 报告显示这块盘的 Configured Capacity 是 3.6T,DFS Used 是 2.0T,DFS Remaining 是 1.2T,算下来 Non DFS Used 大约是 0.4T。但df显示 Used 是 3.3T,中间差了 1.3T。这说明除了 Non DFS Used,还有一部分空间是"账面对不上"的,需要进一步排查。
4.2 逐层定位:找到三个"元凶"
第一步,用du看/data1下各目录占用:
du -h --max-depth=1 /data1 2>/dev/null | sort -rh结果发现 DataNode 的数据目录占了 2.0T,和 DFS Used 对得上。但/data1下还有一个kafka-logs目录,占了 800G。这就是第一个元凶——同盘部署的 Kafka 日志。
第二步,用lsof | grep deleted查已删除未释放的文件,发现一个被删除的日志文件占了 300G,持有句柄的是一个没重启的老进程。这是第二个元凶。
第三步,检查文件系统保留块,发现 ext4 默认保留了 5%,也就是 180G。这是第三个元凶。
三项加起来:800G + 300G + 180G = 1.28T,和之前算出来的 1.3T 差异基本吻合。问题定位完成。
4.3 清理操作与验证
针对三个元凶分别处理:
Kafka 日志方面,先确认 Kafka 的日志保留策略,发现配的是 7 天,但实际没生效,因为清理线程被某个异常阻塞了。重启 Kafka 的清理线程后,手动触发了一次清理,释放了 600G。同时把保留策略调整为 3 天,并加了监控。
已删除未释放的文件方面,确认那个进程不是核心服务后,重启了进程,300G 空间立即释放。如果当时不能重启,我会用truncate的方式处理。
文件系统保留块方面,把数据盘的保留比例从 5% 调到 1%,释放了约 144G。
全部处理完,再次df -h:
Filesystem Size Used Avail Use% Mounted on /dev/sdb1 3.6T 2.3T 1.2T 65% /data1使用率从 97% 降到了 65%,Non DFS Used 也从 0.4T 降到了 0.1T 左右。整个过程没有删除任何 HDFS 数据,全部是通过清理非 HDFS 占用实现的。
4.4 这次实战的几个关键经验
第一,先对齐指标再动手。如果一上来就删 HDFS 数据,不仅解决不了问题,还可能造成数据丢失。第二,du 和 df 的差异是重要线索,差异越大,说明"账外"占用越多。第三,同盘混部是万恶之源,能专用就专用,不能专用就要做好配额和监控。第四,保留块这种"隐形占用"要提前规划,建盘时就把比例调好,别等出问题了再改。
5. 防患于未然:让 Non DFS Used 不再失控
清理是治标,预防才是治本。Non DFS Used 之所以容易失控,根本原因是它不在 HDFS 的管理范围内,没有天然的清理机制。所以必须靠运维规范来兜底。下面是我总结的几条实践建议,都是踩过坑之后沉淀下来的。
5.1 磁盘规划:DataNode 数据盘尽量专用
这是最有效的一条。DataNode 的数据盘就只放 HDFS 数据,不要混部其他服务。如果资源实在紧张,至少要把日志、临时文件这些高增长的东西放到独立的盘上。我现在的习惯是,每台 DataNode 至少配两块盘,一块专门给 DataNode,一块给系统和日志。这样即使日志爆了,也不会影响 HDFS。
如果必须混部,一定要给每个服务设置磁盘配额。Linux 的quota或者 cgroup 的blkio都能做,虽然配置麻烦点,但比事后清理省心得多。
5.2 监控体系:把 Non DFS Used 纳入日常告警
很多人只监控磁盘使用率,不监控 Non DFS Used。这导致问题发现时往往已经很严重了。我的做法是,把 Non DFS Used 的绝对值和使用率都纳入监控,设置两级告警:超过 100G 或者超过磁盘容量 10% 时预警,超过 200G 或者 20% 时严重告警。
采集方式可以用 DataNode 的 JMX 接口,也可以用hdfs dfsadmin -report解析。Prometheus 生态里有现成的 HDFS exporter,配置好就能直接出图。关键是告警阈值要根据集群规模调整,小集群 100G 可能就很大了,大集群可能 500G 才值得关注。
5.3 日志规范:统一用 logrotate,禁止手动 rm
日志是 Non DFS Used 最常见的来源,所以日志管理必须规范。所有服务的日志都要配logrotate,并且要验证轮转生效。轮转策略建议按大小和天数双条件触发,比如单文件超过 500M 或者超过 1 天就轮转,保留 7 天,压缩存储。
还有一条铁律:永远不要手动rm正在被写入的日志文件。要清理就用truncate -s 0,或者通过 logrotate 的copytruncate模式。这样能避免"已删除未释放"的问题。
5.4 定期巡检:每月做一次磁盘占用盘点
再好的自动化也替代不了人工巡检。我建议每月做一次磁盘占用盘点,重点看几个东西:Non DFS Used 的变化趋势、/var/log和/tmp的增长情况、有没有新的服务混部到数据盘上、快照策略有没有产生意外占用。
巡检不用太复杂,写个脚本把关键指标拉出来,人工过一眼就行。关键是形成习惯,别等告警了才去看。我见过太多团队,平时不管,一出问题就是紧急故障,其实很多问题在巡检时就能发现苗头。
5.5 容量规划:给 Non DFS Used 留出预算
做容量规划时,不能只算 HDFS 数据增长,还要给 Non DFS Used 留出空间。我的经验是,至少预留 15% 到 20% 的磁盘空间给非 HDFS 占用。如果混部了其他服务,预留比例还要更高。这样即使 Non DFS Used 增长,也不会立即触发磁盘告警,给处理留出缓冲时间。
具体到每个集群,预留比例可以根据历史数据来定。比如过去半年 Non DFS Used 平均占 8%,峰值到过 15%,那规划时就按 20% 预留,留足安全边际。
6. 几个容易踩的坑和我的应对心得
最后这部分,我想聊几个具体操作中容易踩的坑。这些都是我在实际处理中交过学费的,写出来希望能帮大家少走弯路。
第一个坑是误判 Non DFS Used 为负数。前面提过,这通常是容量配置错误导致的。但还有一种情况是 DataNode 刚启动,块扫描还没完成,指标会短暂异常。这时候不要急着改配置,先等一轮扫描完成。如果扫描完还是负数,再去检查dfs.datanode.data.dir的配置和实际磁盘是否匹配。
第二个坑是用du排查时忽略了挂载点。du默认会跨文件系统统计,如果数据目录下挂了别的盘,du会把挂载点的内容也算进去,导致结果失真。正确的做法是用du -x限制在同一个文件系统内,或者用--exclude排除挂载点。
第三个坑是清理快照时没确认依赖。有些快照是备份策略的一部分,直接删可能导致备份链断裂。清理前一定要确认快照的用途和保留策略,最好和相关负责人确认后再操作。
第四个坑是调整保留块后忘记更新监控。把保留块从 5% 调到 1% 后,磁盘的实际可用空间变了,但监控阈值如果没同步调整,可能会出现误告警或者漏告警。每次调整磁盘相关配置后,都要同步检查监控规则。
第五个坑是在业务高峰期做清理。清理操作本身会消耗 IO,如果在大促或者业务高峰期做,可能影响 HDFS 读写性能。我的习惯是安排在业务低峰期,并且分批操作,不要一次性对所有节点动手。
说到底,Non DFS Used 的清理不是什么高深技术,核心就两点:搞清楚它是什么,按正确的顺序排查。它本质上是一个"差值指标",是 DataNode 算不明白的那部分空间。只要掌握了df和du的交叉验证方法,再配合lsof和文件系统工具,绝大多数情况都能定位到具体原因。真正难的不是技术,而是养成规范的运维习惯——磁盘专用、日志轮转、定期巡检、容量预留。这些做到了,Non DFS Used 就不会再成为半夜告警的常客。