ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Linux磁盘空间排查:用du与df快速定位文件夹占用

Linux磁盘空间排查:用du与df快速定位文件夹占用 先说说这个标题背后的真实场景。绝大多数人搜这句话的时候服务器磁盘已经快满了或者某个业务目录增长异常需要立刻定位到底哪个文件夹在吃空间。我自己处理过的磁盘告警少说也有几十次从一句话的需求出发实际上要解决的是“快速定位占用源头并安全清理”这一整套动作。这篇文章就把我在 Linux 服务器上查看文件夹占用空间、定位大目录、排查空间异常的所有常用方法和踩坑记录整理出来适合刚接触运维的开发者也适合需要临时上服务器救火的业务同学。1. 需求拆解一句话标题背后其实是三件事1.1 你真正想知道的是什么“查看某文件夹占用空间大小”这句话实在太笼统了实践里它至少包含三层需求第一层是想知道某个目录总共有多大比如 /var 已经 50G 了这通常是看一眼就能回答的问题。第二层是定位这个大目录里到底哪个子目录或者哪个文件最占空间这个才是排查磁盘告警的真正核心。如果只是知道 /var 有 50G 却不知道里面是什么等于没查。第三层是判断整个磁盘分区还剩多少可用空间因为有时候你看到的文件加起来并不大磁盘却满了这说明空间挂在别的地方比如其他目录、隐藏文件、或者被删除但仍被进程占用的文件。很多人一上来就敲du -sh 目录名这个命令没问题但它只能回答第一层问题。而真正难处理的是第二层和第三层这也是这篇文章的重点。1.2 两条技术路线到底怎么选Linux 下查看空间占用有两条完全不同的技术路线必须区分清楚。dudisk usage走的是目录树它从指定路径开始逐个遍历目录项和文件累加每个文件实际占用的磁盘块大小。它回答的问题是“这个文件夹里的所有内容加起来占了多少磁盘空间”。dfdisk free走的是文件系统元数据直接读取超级块里的统计信息打印整个文件系统分区的总容量、已用量和剩余量。它回答的问题是“这个分区还有多少空间可用”。两者看似差不多实际经常不一致原因就是统计维度不同。后面第 4 节我会专门展开讲两者不一致的几种情况。先记住一个原则看单个目录或者整个目录树的大小用du看磁盘分区整体剩余空间用df两个必须配合使用。2. du 命令的核心参数与统计原理2.1 为什么 du 显示的数值和 ls 的 size 不一样这是我被问过最多的问题。在某个目录里执行ls -lh看到一个文件显示 100M但du -h 文件名却显示 104M 或者 98M很多人就懵了。原因在于ls -l显示的是文件的“逻辑大小”也就是文件内容的实际字节数而du统计的是文件在磁盘上“物理占用”的块大小。文件系统是按照块block来分配空间的一个块通常是 4K。哪怕一个文件只有 1 个字节它也要占一个完整的 4K 块du就会按 4K 来算。反过来如果是稀疏文件文件中间有大量空洞文件逻辑大小可能很大但实际分配的块很少du统计出来的就比ls -l小很多。所以当你在博文或者文档里看到du的结果和网页上某个文件标注的大小不一样不要觉得是命令用错了这本身就是 Linux 文件系统分配机制的正常表现。你要做的是理解du统计的是“占磁盘空间”而不是“文件有多长”。2.2 必须记住的常用参数组合du的参数不少但日常排查其实只需要记住我下面列出的这几个组合基本覆盖了绝大多数场景。先给出最基本的一些参数说明参数作用使用场景-hhuman-readable以 K/M/G 为单位显示几乎所有场景-ssummarize只显示指定目录的总大小精确查看单个目录-aall同时显示目录和文件需要看到具体文件时--max-depthN最多递归 N 层子目录定位大目录时最常用-c最后显示所有目录的总计统计多个目录的和--excludePATTERN排除匹配模式的文件/目录跳过日志或缓存目录-x不跨越文件系统边界避免统计到挂载的其他磁盘用的时候我建议直接按这样的组合去记# 查看单个目录总大小 du -sh /var # 查看当前目录下所有一级子目录的大小并排序 du -h --max-depth1 . | sort -hr # 如果想连文件一起显示 du -ah --max-depth1 . | sort -hr # 统计多个指定目录的总大小 du -sch /var/log /tmp /home注意du -h --max-depth1和du -sh *执行结果其实差不多但前者更规范。du -sh *的问题在于它会把 command-line arguments 展开后才去统计如果当前目录下文件特别多会有一瞬间的参数列表过长风险而且用*会漏掉隐藏文件这一点后面还要单独说。2.3 用 sort 把结果按大小排序du默认输出是不排序的目录一多根本看不出来谁最占空间。所以必须和sort配合。du -h --max-depth1 /var 2/dev/null | sort -hr | head -20sort -hr的意思是按人类可读的数值4K、100M、2G 这种格式逆序排序。这里有一个关键细节如果你直接用sort -h它会正确识别 K/M/G 单位的大小关系但如果用sort -r普通排序会把带 G 的和带 M 的混在一起排错。我第一次用就犯过这个错误以为 10M 比 2G 大其实是把字符串当字典序排了。另外2/dev/null一定要加上因为du在遍历过程中遇到没有权限的目录会输出du: cannot read directory之类的报错这些错误会混进输出里干扰排序。3. 一套可以直接抄作业的排查流程3.1 第一步用 df 判断整体情况所有磁盘空间排查都应该先从df开始而不是直接du。因为你需要先搞清楚到底是哪个分区满了确认优先级。df -h df -idf -h是查看磁盘容量使用率df -i是查看 inode 使用率。我多次遇到一种情况磁盘容量显示还有 20G 空余但业务报错“No space left on device”这时候df -i会发现 inode 已经 100% 占满了。inode 是文件系统里记录文件元数据的索引节点小文件特别多的目录比如邮件队列、缓存目录、容器镜像层最容易把 inode 耗尽。判断顺序应该是先df -h看容量再用df -i查 inode两个都正常再继续往下走。3.2 第二步从根目录逐层下钻定位整体空间都确认了之后进入定位大目录的环节。第一次先看根目录下所有一级目录的大小因为一般不会一上来就有针对性的目录猜测先看全局状态du -h --max-depth1 / 2/dev/null | sort -hr | head -20输出结果里通常会看到/var、/usr、/home里有一个特别大于是进入对应目录再次执行du -h --max-depth1 /var 2/dev/null | sort -hr | head -20就这样一层一层往下钻。这个过程我自己的经验是“每次只看一级目录”不要试图一步到位直接du -h --max-depth3 /那样的输出会非常长且杂乱因为最大深度 3 会把几百个目录全部列出来。你反而会被扑面而来的信息淹没没法判断重点。逐层下钻虽然多敲几次命令但每次都只需要关注 top 10 的目录思路非常清晰。到某一层出现大量平铺的文件时再改用du -ah把文件也显示出来直接定位到具体文件du -ah --max-depth2 /var/log 2/dev/null | sort -hr | head -203.3 第三步千万别漏了隐藏文件和挂载点这一步是新人最容易踩的坑。du -h --max-depth1统计的是当前目录下所有条目但如果你用du -sh *这种方式shell 的*默认不会匹配以.开头的隐藏文件和目录。比如/home下用户的.cache目录、/var/spool下可能有隐藏目录这些全都会被漏掉。结果就是排查了半天明明某文件夹占用很高但统计结果却对不上。所以我特别建议你执行下面的互补命令专门检查隐藏目录的大小du -h --max-depth1 /要查的目录 2/dev/null | sort -hr du -sh /要查的目录/.[!.]* 2/dev/null第二条命令里的.[!.]*匹配的是以点开头但第二个字符不是点的条目也就是隐藏目录和隐藏文件但不包括.和..。它的作用就是专门揪出被普通通配符漏掉的隐藏文件。遇到过最典型的一次某台服务器/root下面看起来什么都没有结果du -sh /root/.*一查一个.maildump隐藏目录占了 80G就是某个异常任务一直在往里面写临时文件。另外还要注意挂载点。du默认会穿越文件系统边界继续统计比如某个目录下面挂了一个 NFS 或者另一块数据盘du会把挂载点里的内容也算到当前目录头上导致这里显示的占用不代表它占用了你当前这个分区。这时候要加-x参数跳过其他文件系统du -x -h --max-depth1 / 2/dev/null | sort -hr3.4 找到大文件之后的清理验证定位到具体的大文件之后不要冲动地直接rm。先确认这个文件的用途特别是日志文件有些进程会持续写入你手动删掉了但进程还持有文件句柄磁盘空间并不会释放。确认要清理之后在删除前记录一下删前后空间情况df -h /var du -sh /var/log rm -f /var/log/某个大日志 df -h /var du -sh /var/log前后对比一下能确认空间是否真的释放了若删除了但df显示可用空间没有增长则说明文件被某个进程占用了具体处理方式我在第 4 节细说。4. du 和 df 显示不一致的深层原因4.1 文件被删除但进程仍持有句柄这是生产环境最经典的问题你在某个目录里看到一个大文件执行rm删掉了目录空间统计也正常了但df -h一看整个分区的可用空间没有任何变化还是满的。原因在于 Linux 的文件删除机制。rm删除的只是对文件的一个目录项链接但如果有一个进程正打开着这个文件文件仍然存在于磁盘上只是不再有路径可以访问到它。那个进程不关闭这个文件句柄空间就永远不会释放。遇到这种情况用lsof找出持有已删除文件的进程lsof L1L1会列出“链接数小于 1”的文件也就是被删除但仍被打开的文件。输出里找到进程 PID 后要么重启该进程要么让服务方重新加载配置文件。如果是一台临时服务器急着要空间确认进程可以重启就直接重启服务。千万不要为了释放空间去kill -9一个你不知道是什么的进程先看lsof输出里进程名再判断能不能重启。文件被删除但空间不释放的问题在日志类服务、数据库、消息队列上特别常见尤其是一些不会自动logrotate的进程日志被手动删了但 fd 还握着磁盘空间凭空消失几十 G。4.2 根文件系统保留块被算进“已用”里我用df -h查看/分区时经常看到已经用了 95% 以上但du -h --max-depth1 /把所有目录加起来也没那么大中间就差了那么几个 G。这里面的主要原因之一是 ext4 等文件系统默认会预留 5% 的空间给 root 用户用来防止系统因为空间耗尽而无法正常启动。这 5% 的保留块在df里是算在“已用”里面的但du是完全感知不到这部分空间的。所以当你的根分区使用率显示 97%但du统计的总和只有 92% 左右不用太惊讶这 5% 是文件系统层面的保留。如果你确实需要释放这部分空间用下面的命令调整保留比例tune2fs -m 1 /dev/sda1但不建议在生产服务器上随意调整尤其是根分区。这 5% 空间在极端情况下是救命的数据盘的保留比例调低一些倒是无妨。4.3 挂载点、硬链接和稀疏文件的影响再补充几种导致du和df不一致的情况。挂载点嵌套是最常见的误判来源。你明明清理了/data下的一堆文件/data所在分区空间却没变化那是因为/data其实是一个独立挂载的分区你在/data下清理的文件并不属于你df -h时看的那个分区。硬链接会让du重复计算。同一个 inode 的同一个文件被硬链接到多个目录路径下du在遍历每个路径时都会把这份文件的大小加一遍导致总体统计结果比真实占用偏大。统计时若想按真实文件系统占用算可以用-P参数不跟随符号链接但硬链接本身造成的重复统计没有参数能完全规避。还有一个容易忽略的是 NFS 挂载目录的st_nlink显示异常。某些网络文件系统上du会遍历到无穷无尽的目录循环直接导致du卡死甚至报错所以遇到 NFS 目录建议用du -x或者直接跳过不要硬跑。稀疏文件的例子一个 10G 的文件实际磁盘占用可能只有 1G因为文件内部有很多空洞。du统计的是实际分配的块ls -lh显示的是逻辑大小差距巨大是正常的你不要疑惑是不是统计错了。5. 更高效的工具与实用技巧5.1 用 ncdu 做交互式排查如果你完全不想一层层敲命令或者排查的是一个几百 G 的大目录用ncdu体验会好很多。它是基于ncurses的交互式磁盘占用分析工具安装特别简单# Debian/Ubuntu apt install ncdu # CentOS/RHEL yum install ncdu然后直接执行ncdu /var它会先把目录结构扫描一遍然后进入交互界面直接按大小排序展示所有子目录和文件。你可以用上下键移动按回车进入子目录按d删除当前项按g重新扫描。这个工具在做临时上台机器快速排查时确实很顺手所见即所得。不过ncdu也有短板它默认不会扫描挂载点之外的文件系统对于跨分区的目录结构你需要在启动时加参数或者分多次扫描。另外它扫描大目录时耗时会比直接du -h --max-depth1长一些因为它是完整遍历所有子目录。如果只是想快速看一级目录大小我还是推荐命令行du sort的组合。5.2 常用别名与一键脚本排查的空间问题时命令敲多了就希望能少敲几个字。我习惯在自己的 shell 配置里加几个别名这里直接分享给你alias duddu -h --max-depth1 2/dev/null | sort -hr alias dufdu -sh 2/dev/null alias dfhdf -hT这样每次排查只需执行dud /var dud /home dfh如果你经常需要在不同服务器上处理磁盘问题我建议写一个简单的一键定位脚本比如下面这样的bigfind.sh#!/bin/bash # 使用方式./bigfind.sh /var TARGET${1:-/} du -h --max-depth1 $TARGET 2/dev/null | sort -hr | head -20 echo 文件系统使用情况 df -hT $TARGET这个脚本会先列出指定目录下最大的 20 个子目录再显示对应分区的整体情况一次执行就能同时完成“目录内定位”和“分区整体判断”两个任务。5.3 用 --exclude 和 -t 过滤掉不关心的目录在实际的日志服务器上最容易占空间的通常是固定路径比如/var/log、/tmp、某个应用缓存目录。如果你只是想找出那些不常规的大目录可以用--exclude把已知的大目录跳过du -h --max-depth1 / --exclude/proc --exclude/sys --exclude/data --exclude/var/cache 2/dev/null | sort -hr | head -20这样可以把旅行加速的重点放在还没排查过的目录上。另外还有一个-t参数只显示大于指定大小的目录比如du -h -t 1G / 2/dev/null | sort -hr只输出超过 1G 的目录对某些磁盘容量超大的服务器来说也能减少噪音。5.4 针对日志目录的特殊排查技巧日志目录是磁盘占用的重灾区。它的特点是子目录多、文件名带日期、且单个文件可能动辄几十 G。排查日志目录时我更喜欢直接列文件而不是列目录ls -lhS /var/log/*.logls -lhS可以直接按文件大小从大到小列出所有日志文件比先du再逐个看文件名更直观。但在日志目录里发现有超大日志文件时并不建议直接对这个目录做rm操作。正确做法是确认该日志对应的服务优先考虑# 查看日志服务是否支持配置 max size 和 rotation # 例如 rsyslog 的配置文件 /etc/logrotate.d/ # 针对某个具体 log 可以手动触发 logrotate logrotate -f /etc/logrotate.d/rsysloglogrotate可以在保留最新日志的前提下压缩和轮转历史日志避免磁盘空间被一个单调增长的日志文件彻底占满。在日志文件还在增长的场景下手动rm只是治标不治本重点是把轮转策略配置好。6. 常见问题速查表与实操心得6.1 高频问题与解决方案现象可能原因排查命令处理建议du -sh 某目录结果很大但df -h分区明明还很空该目录在挂载的其他分区上或存在跨文件系统的目录df -h 目录名、mount | grep 目录名确认目录所在分区使用du -x跳过其他文件系统删除了大文件df -h可用空间没增加文件被进程持有句柄lsof L1重启对应进程或让进程关闭文件句柄磁盘容量显示没满但系统报 No space left on deviceinode 耗尽df -i统计小文件数量迁移或清理大量小文件所在目录du统计所有目录总和比df已用量小很多文件系统保留块、已删除未释放空间、隐藏文件被遗漏tune2fs -l /dev/sdaX、du -sh /.*根据情况处理删除隐藏目录或检查保留块ncdu显示某目录非常大但du -sh显示不大稀疏文件或符号链接循环ls -lh、ls -l检查 inode根据文件类型对应处理/proc、/sys目录不能直接 du虚拟文件系统排除这两个目录正常现象不要尝试清理6.2 我踩过的一些坑和总结的经验踩坑经历先说说。有次帮同事排查一台编译机磁盘 100% 满所有常规命令都查不出哪里大。我du --max-depth1逐层看每个目录都 正常但加起来才用了不到 60%。最后用lsof L1才发现是一个被删除的编译临时文件被某个还开着的进程一直占住足足 40G。从那以后我排查磁盘问题一定会把lsof L1纳入标准流程而不是最后实在查不出来才想到。还有一次是磁盘没满但某个服务频繁宕机查了半天才发现是/var目录下有一个隐藏的.cache目录被程序无限写入小文件把 inode 耗尽了。所以我现在df -i也基本是必查项。给几点个人的实操建议第一没有把握不要删文件。宁可先du确认内容看到路径名里带tmp、cache、log等明显标识再根据自己对业务的了解决定是否清理。第二清理大日志时推荐truncate而不是rm。对正在写入的日志文件执行truncate -s 0 文件名能把文件清空但不改变文件路径和打开的文件句柄相比rm后重新创建要优雅得多尤其是服务进程并不支持动态重新打开日志文件的情况。第三谨慎使用rm -rf。每次敲rm -rf之前我都强制自己先cd到绝对路径再操作不要用相对路径不要带通配符的模糊匹配去删。磁盘满了很急但越急越要冷静。第四如果你的服务器有监控系统建议对根分区和几个常用分区设置空间使用率告警例如 85% 警告、92% 紧急避免每次都是被动的“磁盘满了才发现”。这个话题说到底就是把du、df、lsof这三板斧用好再配合一层层下钻的排查思路。我不太习惯写那种华丽的技术总结只分享一个最朴素的心得——遇到磁盘问题别慌先看分区再看目录最后看文件句柄按这个顺序走大多数问题都能很快定位。下一次你再看到磁盘告警的时候直接按这篇文章的流程操作就行。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表