多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

磁盘空间排查实战:du命令参数选择与定位技巧

磁盘空间排查实战:du命令参数选择与定位技巧 干运维和用服务器的人基本都遇到过这个经典场景某天监控突然报警说磁盘使用率超过90%或者业务进程开始报No space left on device你连上服务器先敲一句df -h发现根分区已经100%了。然后呢到底是哪个目录、哪个文件把空间吃光的这时候du就是手边最顺手的排查工具。很多人只会du -sh但真遇到几十TB的数据盘、上百万小文件的目录命令参数选不对光是等待扫描就能让你怀疑人生。这篇文章把磁盘空间管理里du命令的常用场景、参数选择、排查实战和踩坑经历都梳理一遍。内容既适合刚入门的Linux用户照着操作也能给已经写过不少命令的老伙计们查漏补缺。我不会罗列一堆命令大全而是用解决实际问题的思路把du这个工具讲透。1. 磁盘空间管理的基本功先搞清楚du到底在算什么1.1 从块到目录du和ls看到的东西不一样很多新手会疑惑为什么我用ls -l看某个文件显示1G用du看同一个文件却显示1.1G这俩命令数出来的东西根本不是同一个维度。ls -l展示的是文件的逻辑大小也就是文件内容本身的字节长度。而du是disk usage的缩写它统计的是文件系统为存放这个文件实际分配的磁盘块数。Linux文件系统以块为单位分配空间一个块通常是4Kext4、xfs默认是4K块。一个只有1字节的文件逻辑大小是1字节但它至少要占一个块也就是4K。所以大量小文件占用的空间会比它们内容总和要大得多。更离谱的是稀疏文件。比如你创建一个文件往里写了1G的空字节但故意在文件系统层面没有分配块ls -lh会显示1Gdu -sh却可能显示0或者很小。如果你只凭ls的大小去做清理计划很容易误判。生产环境里数据库文件、虚拟机磁盘镜像、日志文件都可能出现这种情况。目录本身在Linux里也是一个文件里面记录着文件名与inode的对应关系。du统计一个目录时会把这个目录文件自身占用的块也一并算进去。所以du -sh /var的结果实际上是/var目录树里所有文件、子目录、隐藏文件、特殊文件比如socket、fifo实际占用磁盘块的总和。1.2 du的统计范围跨文件系统与挂载点du默认会跟随路径往下走如果某个路径下挂载了其他文件系统它也会一并统计。举个例子你的根分区/已经用了80%你执行du -sh /想看根目录下是什么占空间。结果发现/mnt/data是一个独立的2T数据盘挂载点du会把那2T数据也全部算到/的统计结果里。这时候你得到的是一个混合数字根本无法定位根分区自己的问题。解决办法是-x参数也叫--one-file-system意思是只在同一个文件系统内进行统计跨文件系统的挂载点直接跳过。排查根分区磁盘满时我的习惯是固定使用du -h -x --max-depth1 /这样可以只看根分区自身内容把/proc、/sys、/dev、/tmp下的tmpfs以及其他挂载点全部排除掉统计结果才真正反映根分区的压力。1.3 软链接、硬链接与重复计算软链接symlink默认不会被du跟随它只统计软链接文件自身的块大小。只有当你显式加-L参数时du才会顺着软链接去统计目标文件或目录的大小。其实日常排查中我基本不用-L因为软链接指向的内容通常在其真实路径下你直接统计那个真实路径就够了加了-L反而可能把同一个目录重复统计得出离谱的结果。硬链接是另一个坑。硬链接和原文件指向同一个inode也就是同一份磁盘数据只是多了个文件名。du在扫描一个目录树时内部会有个去重机制同一个inode遇到第二次的时候会跳过不会重复计算。但是如果你把两个目录分开执行du -sh再拿结果做减法就可能把同一个硬链接文件在两个目录里各算一次最终导致判断失真。最稳妥的做法是不要在两个目录上分别du然后相减而是从它们的共同父目录开始一次du -sh整体看完。2. du命令的实操套路与参数选择2.1 最常用的组合du -sh和du -h --max-depth1先说最核心的三板斧。第一板斧是看整体大小du -sh /data-s表示汇总summary只输出总计一行-h是human-readable自动转换成K、M、G等单位。这个命令适合快速了解一个目录的总空间占用。第二板斧是找大头目录du -h --max-depth1 /var--max-depth1表示只显示指定目录下第一层子目录的大小不会无限向下递归展示。输出结果大概是4.0K /var/spool 120M /var/log 8.0K /var/mail 2.3G /var/lib ...这样你一眼就能看出/var/lib是元凶然后进去继续用同样的命令下探du -h --max-depth1 /var/lib这种逐层下探的模式是定位大目录最快的方式。--max-depth2可以一次性看到孙层目录但输出行数会爆炸一般排查环境不推荐。第三板斧是列表式对比du -sm /var/* | sort -rn | head -20-m以MB为单位输出整数再用sort -rn按数字倒序直接把最大的20个条目顶到前面。这在看大量目录时效率极高。2.2 排除不需要的目录--exclude有些时候你知道某些目录不用管但du还是会老老实实地扫进去。比如统计网站根目录/var/www时你想排除里面的备份目录/var/www/backup可以用du -sh --exclude/var/www/backup /var/www--exclude支持通配符排除所有日志文件也可以这么写du -sh --exclude*.log /var/log注意--exclude和-x是两回事。-x是跨文件系统跳过--exclude是路径模式匹配跳过。排查根分区时我经常两个一起用du -h -x --max-depth1 --exclude/proc --exclude/sys /虽然-x本来就会跳过这些挂载点但明确写出来更清楚也防止有些伪文件系统没挂载但路径存在的情况。2.3 统计inode占用du --inodes磁盘满不只有空间满还有inode满这一种形态。inode是文件系统里记录文件元数据的索引节点每一个文件或目录都会消耗一个inode。当你的分区里充满了大量几字节的小文件可能空间还剩很多但inode已经用完了同样写不进新文件。遇到这种情况df -i会显示inode使用率100%。怎么定位是哪个目录生成了海量文件用du加--inodes参数du --inodes -h --max-depth1 /data它会统计每个目录下所有条目文件子目录的inode占用数量。输出大体是123456 /data/tmp 2456 /data/cache ...看到/data/tmp有十几万条目基本就锁定了问题来源通常是临时文件或未清理的缓存。2.4 单位与精度控制-b、-m、-kdu的输出单位默认是1024字节的块数但不同环境下可能受环境变量影响不够直观。所以实际使用中我很少不加-h偶尔需要精确字节数的时候会用-bdu -sb /data-b等价于--block-size1显示精确到字节的总数。如果你想用固定单位已经知道目标大概是几百M可以用-m强制以MB输出省去sort -n时因为K、M、G混合排序出错的问题。du -m --max-depth1 /data | sort -rn | head如果目录大到GB级别用-g也很方便。这些参数本质上都是设置--block-size你完全可以写--block-size1M来强制以M显示效果一样。2.5 用du的时间维度做变化趋势--time磁盘是某个具体时间点开始满的如果能知道哪些目录在那段时间有新文件写入排查效率会高很多。du有个容易忽略的参数--timedu --time -h --max-depth1 /var它会在每条结果后面显示该目录内所有文件中最新的mtime文件内容修改时间。展示效果类似2.3G 2025-04-07 14:22 /var/lib 120M 2025-04-07 16:30 /var/log ...通过时间戳你能快速锁定昨天半夜突然写入大量数据的目录。不过注意这里记录的是目录内文件的最后修改时间不是目录本身的最后修改时间。这个参数不常被提到但在事后追溯异常写入来源时非常有用。3. 实战磁盘空间排查完整流程3.1 先df确定挂载点水位处理磁盘满问题第一步永远是df不应该是du。因为df直接读取文件系统的超级块和元数据能瞬间告诉你每个挂载点的使用率。du需要遍历目录树是个慢操作在还不确定问题盘是哪块的时候不要贸然全盘扫描。我常用的检查命令df -hT-T会显示文件系统类型比如ext4、xfs、tmpfs、overlay。容器环境里更要注意区分docker overlay2的镜像层也会占空间它通常在/var/lib/docker下面通过df -h能看到overlay挂载点。再补一句df -i看inode使用率。如果/dev/sda1的IUse%接近100%那就别走空间排查路线了直接按3.2里--inodes的思路去找目录。很多人只知道df看空间忽略了df -i但inode耗尽和空间耗尽的表象完全一样都是No space left on device排查方向完全不同。3.2 用du逐层定位大目录确定目标分区后从挂载点开始第一层扫描。假设df -h显示/分区使用率95%执行du -h -x --max-depth1 / 2/dev/null这里加2/dev/null是为了屏蔽权限不足的报错否则输出会被大量Permission denied刷屏。扫描结果大概长这样0 /proc 0 /sys 1.2M /etc 45G /var 12G /usr 3G /home ...明显/var是最大的继续下探du -h -x --max-depth1 /var 2/dev/null发现/var/log/journal占到38G。接着再往下du -h -x --max-depth1 /var/log/journal或者更直接ls -lhS /var/log/journalls -lhS是按文件大小倒序列出文件对已经明确到比较浅的层级时比du还快。整个定位过程就是df确认盘du找目录ls找文件三步。3.3 大文件的精确定位du与find配合du擅长统计目录聚合大小但你最终要抓出具体的超大文件还是要靠find。我常用的命令是这样find /data -xdev -type f -size 100M -printf %s %p\n | sort -nr | head -20解释一下各个部分-xdev限定在当前文件系统和du的-x同理避免扫到挂载盘。-type f只看普通文件不包含目录。-size 100M筛选大于100M的文件数值可以按需调整。-printf %s %p\n按字节数 路径格式输出每个一行。sort -nr按字节数从大到小排序。head -20只取前20条。输出样例31562340864 /data/app.log 128849018880 /data/db.sql ...记得-xdev一定带上不然find会跨挂载点你在根分区找文件却扫到数据盘上白白浪费时间。还有一种方式是du -adu -a --max-depth1 /data 2/dev/null | sort -rn | head-a会连同文件一起输出不再只是目录。但du -a的排序结果会混入大量目录条目而且默认单位可能是块不够直观。我更推荐find加-printf的组合性能上两者差不多但find的格式可控性更强。3.4 清理手段和删除技巧找到大文件后不要急着rm -rf。先确认这个文件是不是正被进程占用。特别是日志文件服务进程打开着文件句柄你rm掉文件名空间不会立刻释放因为进程还持有那个被删除文件的inode。磁盘使用率依然100%你以为删了实际全白干。排查被占用但已删除的文件lsof L1L1会列出所有link count为0但还被进程打开的文件。看到输出后比如java 12345 user 2w REG 253,1 1568000 1048576 /tmp/.tmp (deleted)确认这个文件是某个java进程留下的处理方式通常是重启这个进程或者确认后直接kill空间才会真正释放。如果是仍在使用的日志文件正确做法是清空而不是删除truncate -s 0 /var/log/app.logtruncate -s 0把文件大小截断为0文件句柄还在进程能继续往里写空间立刻回收。比rm再重启进程安全得多。对于目录中成千上万的小文件不要直接rm -rf整个目录。目录项过多时rm -rf会先遍历挨个删除很慢而且万一中途出错容易留下半个目录。可以先删除内部文件再删目录。也可以用find /data/tmp -type f -deletefind -delete的删除速度和资源占用都比rm -rf好一点但这属于优化项不是强制的。3.5 一分钟定位占空间最大目录的懒人脚本每次手动敲那一长串命令有点烦我把常用的逻辑写成了一个脚本直接放进sit或~/.bashrc里function topdir() { local target${1:-.} du -h -x --max-depth1 $target 2/dev/null | sort -hr | head -20 }用法很简单topdir /var topdir .sort -hr是按人类可读的数值排序GNU sort支持-h参数识别K、M、G后缀。输出直接就是带单位的倒序列表。再配一个按文件找大文件的函数function bigfile() { local target${1:-.} local size${2:-100M} find $target -xdev -type f -size $size -printf %10s %p\n | sort -rn | head -30 }调用示例bigfile /data 200M这样排查磁盘问题基本就是两条命令的事了。4. 常见问题与排查技巧实录4.1 du为什么扫半天不出来这是被问得最多的一个问题。du的本质是递归读取目录项路径下文件数量越多扫描时间越长。如果你有一个目录存了500万个小文件du -sh /data可能要跑十几分钟。优化的思路有这几条一是限制深度前面已经提过。二是用-x把不需要关心的挂载点排除尤其NFS挂载盘扫描远程网络盘的速度完全取决于网络会慢到让你崩溃。三是用ionice降低IO优先级避免临时扫盘影响线上业务的磁盘性能ionice -c3 du -sh /data-c3代表idle级别只有在磁盘没有其他IO请求时才跑对业务影响最小。四是把结果缓存起来而不是反复扫描。比如你需要在一天内多次排查同一个大目录第一次扫描完把结果存文件du -h --max-depth1 /data /tmp/du_result.txt 2/dev/null后面直接查看文件或用grep筛选省掉80%时间。4.2 权限不足导致统计不准确普通用户执行du -sh /var/cache时如果没权限读取里面的子目录du会输出一条Permission denied然后继续统计它能读到的部分。最终结果偏小是必然的。遇到这种场景正确姿势是用sudosudo du -h -x --max-depth1 /如果你是排查线上机器又没有sudo权限那只能接受这个偏小结果同时知道哪些路径没被统计到。2/dev/null会把所有报错信息吞掉方便看结果但不要因此忽略权限缺口。4.3 du显示大小与ls -l不一致是正常的吗正常而且太正常了。前面讲过ls -l是逻辑大小du是物理占用块数。两者的区别在以下场景特别明显大量小文件du显示远大于ls各文件大小之和。稀疏文件du显示远小于ls的大小甚至接近0。日志文件被频繁写入后又被截断ls和du都可能显示一个非典型的当前状态。做空间清理时应该以du的统计为准因为磁盘能不能放下数据看的是物理块是否够用不是逻辑字节是否够。4.4 du统计出的大小和df不一致du /data汇总整个目录树得到的总和df /data看的是整个文件系统已用空间。两者天然会存在差异常见原因文件系统元数据。ext4和xfs的inode表、日志、位图等都要占用空间这部分属于文件系统的开销du统计不到。保留块。ext4文件系统默认保留5%空间给root用户df使用率会因为这5%显得偏高。已删除但未释放的文件。这就是4.2提到的经典问题文件被删了但进程还占着句柄df认为空间被占用du统计目录时却找不到这个文件。如果df显示100%du统计根目录只有50%优先怀疑是不是有大量deleted文件占用。检查方法lsof L1 | grep deleted找到后按情况重启进程或kill进程。一般情况下处理完deleted文件df的数字就会回落。4.5 硬链接导致的误判曾经有个同事统计项目目录时发现明明没有多少数据du -sh /project_a和du -sh /project_b加起来却比磁盘总容量还大。最后查出来是两个目录硬链接了同一个大文件单独看每个目录都完整带上了这个文件的容量从父目录统一看时才会被去重。这个案例给我们的经验是判断多个目录的容量对比时尽量从它们的共同父目录一次统计不要先分别统计再相加减。如果非得分开统计遇到硬链接场景要注意结果可能偏大进一步核实的时候用ls -l看一下文件的link count。4.6 目录明明没看到大文件du却显示很大这种情况常见于三类原因。第一隐藏文件。du默认统计所有隐藏文件但你在ls时忘了加-a自然觉得目录空空实际.cache、.npm、.local里的内容可能已经吃了几G。排查时用ls -la看隐藏项。第二子目录挂载掩盖了真实内容。如果某个目录曾经有数据后来又挂载了新磁盘在这个目录上新磁盘会掩盖掉旧内容。du不加-x时会把挂载盘内容整个算进来你看着像目录大其实是底下挂了个大文件系统。反过来你把挂载点umount掉原本被掩盖的旧文件就又会冒出来占用空间一样被统计到。这种情况风险管理上叫挂载点下面有藏宝清理前先确认挂载关系再动手。第三被删除但仍被进程持有的文件这在4.4里已经细说。4.7 从du到完整的磁盘空间管理习惯du命令本身只是排查工具真正根治磁盘问题是管理习惯。给你几个我实践下来有效的办法。定时输出du快照。写一个cron任务每天凌晨把关键目录的大小保存到带日期的文件0 2 * * * du -h --max-depth1 /data /var/log/diskusage/$(date \%F).txt 21磁盘快满时直接翻历史文件看哪个目录在某天突然增长比事后补救高效太多。安装ncdu作为交互式补充。ncdu本质上是一个增强的du它先从根目录扫描然后给你一个终端交互界面可以用方向键上下浏览、下钻子目录、甚至可以按大小排序还能直接删除选中的目录。如果服务器允许装apt install ncdu或yum install ncdu都很简单。我在内网机器上常用它做深度排查比纯命令舒服很多。配合日志轮转和容器日志限制。很多运维事故不是业务bug而是日志没人管。logrotate配置好按大小或日期切割docker容器启动时加--log-opt max-size100m --log-opt max-file3就能把绝大多数日志暴涨问题保质保量地压制住。最后再分享一个我个人的小体会清理磁盘空间时永远不要把df恢复当作唯一目标。要顺手记录一下到底是谁、什么时候、往哪里写了数据从根源上把问题堵住。du给你的只是结果真正值钱的是你通过它定位到的那条异常写入路径。多花两分钟把这条路径扒出来打个补丁下次爆炸时间就能拖得更远。
返回列表