资讯动态

Linux文件大小统计全解析:du与df命令原理、差异及磁盘排查实战

发布时间:2026/10/23 1:26:58 来源:尧图企业网站定制
1. 文件大小统计到底在统计什么很多人第一次接触 Linux 文件大小统计脑子里冒出来的就是du -sh和df -h这两条命令。但真到了实际工作里你会发现事情远没有这么简单。比如你查一个目录占了 80G删掉里面一个 30G 的日志文件再查还是 80G——这时候你才意识到文件大小统计这件事统计口径不同结果能差出十万八千里。我自己在运维和数据处理岗位上待了十多年被文件大小坑过的次数两只手数不过来。有一次线上磁盘告警我用du查到某个日志目录占了 200 多个 G结果把里面最老的文件删了之后df显示的可用空间几乎没变。后来才搞明白那个文件还被一个没重启的进程攥着句柄空间根本没释放。这件事让我彻底明白文件大小统计不是背几条命令就完事你得搞清楚你统计的到底是磁盘占用还是文件逻辑大小是当前目录树还是单个文件是表观大小还是实际占块。这篇文章就是把我这些年踩过的坑、总结出来的命令组合和排查思路系统地梳理一遍。不管你是刚接触 Linux 的新手还是天天跟服务器打交道的老手都能从里面找到能直接抄作业的东西。我会从最基础的概念讲起把du、df、ls、find、stat这几个核心工具的参数掰开揉碎再讲清楚它们之间的区别和适用场景最后给出一套完整的磁盘空间排查流程。看完你至少能做到面对一个磁盘告警知道先查什么、再查什么每一步的结果意味着什么而不是对着一堆命令瞎试。2. 先搞懂三个核心概念不然命令白敲在动手敲命令之前有几个概念必须先理清楚。这些概念看起来简单但它们是所有文件大小统计命令行为差异的根源。搞不懂这些你敲出来的数字就是一笔糊涂账。2.1 逻辑大小、物理大小和磁盘占用一个文件的大小在不同语境下有三个不同的含义。逻辑大小也叫表观大小apparent size就是文件里实际有多少字节的数据。你写进去 1000 个字符逻辑大小就是 1000 字节。ls -l显示的就是这个值。物理大小是文件在磁盘上实际占用的块数乘以块大小。文件系统不是按字节存储的而是按块block分配的。一个块通常是 4KB。你写一个 1 字节的文件它也要占一个块物理大小就是 4KB。du默认显示的是这个值。磁盘占用这个概念跟物理大小基本是一回事但在有稀疏文件sparse file和硬链接的情况下会有区别。稀疏文件逻辑大小可能很大但实际占的块很少。硬链接则是多个文件名指向同一个 inodedu默认只算一次。用一个生活化的类比逻辑大小就像你快递箱里实际装了多少东西物理大小就像这个箱子占了多大地方。你寄一个戒指箱子本身还是那么大占的地方不会因为戒指小而变小。提示du和ls显示的大小经常对不上绝大多数情况就是因为一个算的是物理块一个算的是逻辑字节。这不是 bug是设计如此。2.2 inode 与块分配的关系Linux 的 ext4、xfs 等文件系统每个文件都有一个 inode里面记录了文件的元数据权限、时间戳、大小、数据块的位置等等。文件名本身不存数据它只是一个指向 inode 的链接。这就引出了两个关键点。第一硬链接是多个文件名指向同一个 inode所以du统计时如果不加特殊参数同一个 inode 只会被算一次。第二inode 的数量是有限的磁盘空间没满但 inode 用完了照样写不进新文件。df -i就是查 inode 使用情况的。块分配还有个细节文件系统在分配块时可能会预分配一些额外的块或者因为碎片导致实际占用比理论值大。这就是为什么有时候du算出来的总和跟df显示的已用空间对不上。2.3 挂载点与跨文件系统统计du默认不会跨越文件系统边界。什么意思呢假设/data是一个独立挂载的磁盘你在/目录下执行du -sh /它统计到/data这个挂载点时就会停住不会把/data里的内容算进去。这个设计是为了避免统计到网络挂载或者别的磁盘上导致结果混乱。但有时候你恰恰需要跨文件系统统计那就得加-x参数的反面——实际上du默认就是跨的-x才是限制在同一个文件系统内。这里容易记反我后面会专门讲。df则是按挂载点来显示的它直接读文件系统的超级块信息所以它显示的是整个文件系统的使用情况跟du的统计口径完全不同。这也是为什么du和df的结果经常对不上后面会详细分析。3. du 命令目录统计的主力工具du是 disk usage 的缩写它是目录级别文件大小统计最常用的工具。但很多人只会du -sh其实它的参数组合能解决非常多具体问题。3.1 基础用法与常用参数组合先看最基础的几条# 查看当前目录总大小 du -sh # 查看当前目录下每个子目录的大小 du -sh * # 查看指定目录的总大小 du -sh /var/log # 按大小排序找出最大的前10个 du -sh * | sort -rh | head -10这里有几个参数需要重点说明-ssummary只显示总计不列出子目录。-hhuman-readable以 K、M、G 为单位显示方便阅读。-aall连普通文件也一起列出不只是目录。-c在最后额外输出一个总计行。--max-depthN限制递归深度N1 表示只统计当前目录下一层。我个人的习惯是排查磁盘问题时先用du -h --max-depth1 /path | sort -rh这样能快速定位到是哪个一级子目录占了大头然后再逐层深入。这比一上来就du -sh *要高效得多因为--max-depth不会把整个目录树都遍历一遍再汇总它在遍历到指定深度就停了。3.2 按大小排序与筛选大文件找出大文件是磁盘排查的核心动作。除了上面说的sort -rh还有几种组合很实用# 找出当前目录下大于100M的文件 find . -type f -size 100M -exec du -h {} | sort -rh # 找出当前目录下最大的20个文件包括子目录 du -ah . | sort -rh | head -20 # 只统计普通文件排除目录 find . -type f -printf %s %p\n | sort -rn | head -20最后这条find -printf的写法值得说一下。%s是文件大小字节%p是路径。它直接输出字节数排序更精确不受du块对齐的影响。如果你需要精确到字节的排序这个写法比du更合适。注意du -ah在文件数量巨大的目录下会非常慢因为它要遍历每一个文件。如果只是想找大文件用find -size 100M先过滤一遍会快很多。3.3 排除特定目录与限制递归深度实际排查时有些目录我们是不关心的比如.git、node_modules、缓存目录等。du提供了--exclude参数# 排除 node_modules 和 .git 目录 du -sh --excludenode_modules --exclude.git * # 排除所有以 . 开头的隐藏目录 du -sh --exclude.* *--exclude支持通配符但要注意 shell 本身也会展开通配符所以最好用引号包起来避免被 shell 提前处理。限制递归深度用--max-depth这个参数在排查大目录时非常关键。比如一个项目目录下有几十万个文件你直接du -sh可能要跑好几分钟但du -h --max-depth1可能几秒钟就出结果了因为它不需要汇总所有子目录的总和只需要算到第一层。3.4 处理硬链接与稀疏文件的统计偏差前面提到du默认对硬链接只算一次。如果你确实需要每个链接都算可以用-l参数# 每个硬链接都单独计算 du -shl /path但大多数情况下你不需要这么做因为硬链接指向的是同一份数据重复计算反而会让结果虚高。稀疏文件的情况更特殊。比如用dd创建的一个 10G 文件如果它是稀疏的实际可能只占几百 K。du显示的是实际占块ls -l显示的是逻辑大小 10G。要查看稀疏文件的真实占用用du -h sparse_file ls -lh sparse_file两个结果对比一下差距就是稀疏的程度。这个知识点在容器镜像和虚拟机磁盘文件的管理中特别重要因为那些文件经常是稀疏的。4. df 命令文件系统级别的空间视图df和du是磁盘排查的两把利器但它们看问题的角度完全不同。du是从文件出发自下而上累加df是从文件系统出发直接读超级块自上而下报告。理解这个区别是解决为什么两个数对不上这个经典问题的关键。4.1 df 的输出到底怎么读先看一条典型的df -h输出Filesystem Size Used Avail Use% Mounted on /dev/sda1 100G 80G 20G 80% / /dev/sdb1 500G 200G 300G 40% /data tmpfs 16G 1.2G 15G 8% /dev/shm每一列的含义Filesystem设备名或文件系统标识。Size文件系统总容量。Used已使用空间。Avail可用空间。注意这个值不一定等于 Size 减 Used因为 ext4 默认给 root 保留了 5% 的空间。Use%使用百分比。Mounted on挂载点。那个 5% 的保留空间是个常见坑。普通用户看到 Avail 是 20G但实际能写的可能只有 15G因为另外 5G 是留给 root 应急的。用tune2fs -m 0 /dev/sda1可以调整这个比例但生产环境不建议随便改。4.2 df 与 du 结果不一致的五大原因这是被问得最多的问题为什么du -sh /算出来 60Gdf显示用了 80G差了 20G 去哪了原因通常有这几个原因说明排查方法已删除但被进程占用的文件文件删了进程还开着句柄空间不释放lsof L1或lsof | grep deleted文件系统保留块ext4 默认保留 5% 给 roottune2fs -l /dev/sda1 | grep Reserved挂载点覆盖挂载点下的原目录被新文件系统覆盖原数据不可见但仍占空间卸载后查看原目录硬链接重复计算差异du 默认去重df 不去重对比du -l的结果文件系统元数据inode、日志等占用的空间一般较小可忽略其中最常见的就是第一条。一个进程打开了某个大日志文件你rm删掉了它但进程没重启文件句柄还在空间就一直被占着。df显示空间没释放du又找不到这个文件因为目录项已经没了。解决办法是找到那个进程重启它或者用 /proc/PID/fd/N的方式清空文件内容。具体操作后面会讲。4.3 查看 inode 使用情况磁盘空间没满但写不进文件很可能是 inode 用完了。用df -i查看df -i输出里的 IUse% 就是 inode 使用率。如果这个值到了 100%即使df -h显示还有空间你也创建不了新文件。inode 被大量小文件耗尽是很常见的情况比如缓存目录里堆了几百万个小文件。排查哪个目录 inode 用得多# 统计当前目录下每个子目录的文件数量 for d in */; do echo $(find $d -type f | wc -l) $d; done | sort -rn | head -105. ls、stat、find单文件与批量统计的利器du和df解决的是目录和文件系统级别的问题但很多时候你需要看单个文件的精确信息或者按条件批量筛选文件。这时候ls、stat和find就派上用场了。5.1 ls 显示的大小为什么和 du 不一样ls -l显示的是逻辑大小du显示的是物理占用。前面已经讲过原理这里举个具体例子$ echo hello test.txt $ ls -l test.txt -rw-r--r-- 1 user user 6 Jan 1 00:00 test.txt $ du -h test.txt 4.0K test.txt文件里只有 6 个字节但du显示 4K因为它占了一个 4K 的块。这就是逻辑大小和物理大小的区别。ls还有一个常用参数-s它显示的是块数ls -ls test.txt 4 -rw-r--r-- 1 user user 6 Jan 1 00:00 test.txt第一列的 4 就是占用的块数默认块大小是 1K所以是 4K。5.2 stat 查看精确的字节数与块数stat是最精确的查看工具它把逻辑大小和物理块数都列出来了stat test.txt输出里关注这几行Size: 6 Blocks: 8 IO Block: 4096 regular fileSize 是逻辑字节数。Blocks 是占用的 512 字节块数8 乘 512 等于 4096 字节也就是 4K。IO Block 是文件系统的块大小。stat还支持格式化输出方便脚本处理stat -c %s %b %n test.txt%s是逻辑大小%b是块数%n是文件名。这个格式在批量统计时很好用。5.3 find 按大小筛选文件的实战写法find的-size参数支持按大小筛选单位有c字节、kKB、MMB、GGB# 找出大于100M的文件 find /path -type f -size 100M # 找出小于1k的文件 find /path -type f -size -1k # 找出大小在10M到100M之间的文件 find /path -type f -size 10M -size -100M注意和-的含义100M是大于 100M-100M是小于 100M不带符号是精确等于。但精确等于在实际中很少用因为文件大小很难刚好等于某个值。结合-exec可以输出大小和路径find /path -type f -size 100M -exec ls -lh {} \; | awk {print $5, $9}或者用-printf更高效find /path -type f -size 100M -printf %s %p\n | sort -rn-printf不需要为每个文件启动一个子进程在文件数量多的时候性能优势明显。6. 实战一套完整的磁盘空间排查流程前面讲的都是单个工具和参数现在把它们串起来形成一套可复用的排查流程。这套流程我在生产环境用过很多次基本能覆盖 90% 以上的磁盘空间问题。6.1 第一步用 df 定位问题分区收到磁盘告警或者发现写入失败第一件事是看哪个分区满了df -h df -idf -h看空间df -i看 inode。两个都要看因为空间满和 inode 满是两种不同的问题解决方法也不一样。假设发现/data分区使用率 95%接下来就针对这个分区排查。6.2 第二步用 du 逐层定位大目录进入问题分区逐层往下查cd /data du -h --max-depth1 | sort -rh找到最大的那个子目录比如logs再进去cd logs du -h --max-depth1 | sort -rh重复这个过程直到定位到具体的大文件或大目录。这个过程通常两三层就能找到目标。如果目录下文件特别多du跑得慢可以先用find快速找大文件find /data -type f -size 500M -printf %s %p\n | sort -rn | head -206.3 第三步处理已删除但未释放的文件如果du算出来的总大小远小于df显示的已用空间大概率是有文件被删了但进程还占着。用lsof查lsof L1 lsof | grep deletedL1是列出所有 link count 小于 1 的文件也就是已经被删除但还有进程打开的文件。输出里会显示进程名、PID、文件大小。处理方式有两种。一是重启那个进程空间自然释放。二是不重启的情况下清空文件# 假设 PID 是 12345文件描述符是 3 /proc/12345/fd/3第二种方式要小心直接 /proc/PID/fd/N会清空文件内容如果那个文件还在被写入可能会导致数据不一致。更安全的做法是先用truncatetruncate -s 0 /proc/12345/fd/3但本质上都是让文件大小归零释放磁盘空间。6.4 第四步清理与验证找到大文件后根据情况决定是删除、压缩还是迁移。删除前一定要确认文件不再需要尤其是日志文件最好先备份或者确认可以丢弃。清理完之后再跑一次df -h验证空间是否释放。如果没释放回到第三步检查是不是还有进程占着句柄。提示删除大文件时如果文件正在被写入直接rm可能导致写入进程报错。更稳妥的做法是先停掉相关服务或者用truncate清空而不是删除。7. 常见问题与避坑指南这一节整理一些高频问题和容易踩的坑都是实际工作中反复遇到的。7.1 为什么 du 和 df 的结果总是对不上这个问题前面分析过原因这里给一个快速排查的决策表现象可能原因快速验证du 小于 df差距较大已删除未释放文件lsof L1du 小于 df差距约 5%文件系统保留块tune2fs -l看 Reserved block countdu 大于 df硬链接被重复计算或跨文件系统检查du -l和挂载点两者都正常但写不进inode 耗尽df -i7.2 统计速度太慢怎么优化du在文件数量巨大的目录下会非常慢因为它要stat每一个文件。优化思路有几个用--max-depth限制深度减少遍历量。用find -size先过滤只对符合条件的文件做统计。用--exclude排除不关心的目录。如果只是想知道大概可以用df看文件系统级别的数据跳过du。还有一个技巧是用ncdu这个工具它是交互式的基于du但做了缓存和增量显示体验好很多。不过它需要额外安装在只有基础命令的环境里用不了。7.3 特殊文件系统的统计注意事项不同的文件系统在统计行为上可能有差异。比如tmpfs基于内存df显示的是内存使用量重启就没了。网络文件系统du统计可能很慢因为要跨网络请求元数据。overlayfs容器常用的联合文件系统du统计的是合并后的视图底层层的文件可能被重复计算。btrfs支持快照和压缩du显示的是未压缩的逻辑大小df显示的是实际占用差距可能很大。在这些文件系统上做统计最好先了解其特性不要想当然地套用 ext4 的经验。7.4 批量统计脚本的编写要点如果需要定期统计并记录文件大小写个脚本是必要的。几个要点#!/bin/bash # 统计指定目录下各子目录大小输出到日志 TARGET_DIR/data LOG_FILE/var/log/disk_usage.log DATE$(date %Y-%m-%d %H:%M:%S) echo $DATE $LOG_FILE du -h --max-depth1 $TARGET_DIR | sort -rh $LOG_FILE注意事项用绝对路径避免脚本在不同目录下执行结果不一致。加上时间戳方便对比历史数据。如果目录很大考虑用nice和ionice降低优先级避免影响业务。输出重定向到日志文件不要直接打印到终端方便后续分析。8. 几个提升效率的进阶技巧最后分享几个我平时用得比较多的进阶技巧能让文件大小统计这件事更顺手。8.1 用 awk 做单位换算和汇总du的输出单位不统一有时候需要统一换算成 MB 或 GB 再汇总。用awk可以搞定du -sk * | awk {sum $1} END {print sum/1024 MB}-k让du以 KB 为单位输出awk累加后除以 1024 得到 MB。这个写法在需要汇总多个目录大小时很有用。8.2 结合 watch 做实时监控排查磁盘增长问题时可以用watch定期刷新watch -n 5 du -sh /data/* | sort -rh | head -10每 5 秒刷新一次能直观看到哪个目录在增长。不过du本身有开销刷新频率不要设太高5 到 10 秒比较合适。8.3 用 tree 或 ncdu 做可视化如果环境允许安装额外工具ncdu是文件大小统计的神器。它提供交互式界面可以像文件管理器一样浏览目录大小还支持直接删除文件。安装方式因发行版而异一般包管理器里都有。tree命令加-h和--du参数也能显示目录大小tree -h --du /data但tree在目录很大时输出会非常长不如ncdu直观。8.4 日志轮转与大小控制很多磁盘问题根源是日志没有轮转。配置好logrotate能从源头上避免日志撑爆磁盘。一个典型的配置/data/logs/*.log { daily rotate 7 compress delaycompress missingok notifempty copytruncate }copytruncate这个选项特别重要它先复制日志再清空原文件避免重启服务。对于不能重启的进程这个选项是必须的。我在实际使用中发现磁盘空间排查这件事工具和命令只是表面真正重要的是理解文件系统的工作原理和统计口径的差异。同样一条du -sh在不同场景下结果的含义可能完全不同。把这些底层逻辑搞清楚了遇到再奇怪的现象也能一步步推导出原因而不是靠猜。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑