当前位置:首页>Linux>Linux 磁盘满了别急删文件:这 8 个排查命令先跑一遍

Linux 磁盘满了别急删文件:这 8 个排查命令先跑一遍

  • 2026-10-11 08:17:59
Linux 磁盘满了别急删文件:这 8 个排查命令先跑一遍

Linux 磁盘满了别急删文件:这 8 个排查命令先跑一遍

问题背景

磁盘空间告警是 Linux 服务器最常见的报警之一。很多人的第一反应是登录服务器直接 rm -rf 找大文件删掉。但实际场景中,"磁盘满"往往有三种不同情况:

  • 磁盘空间真的满了(df -h 显示 100%)
  • inode 耗尽了(df -h 显示还有空间,但系统报 No space left on device)
  • 文件已删除但空间未释放(df 显示满,但 du 统计不到大文件)

不分清楚这三种情况就盲目删文件,可能删了半小时空间也没释放,或者删了不该删的文件导致业务异常。

本文按排查顺序逐一讲解 8 个关键命令,覆盖空间排查、inode 排查、已删文件句柄排查、IO 性能排查,最后给出生产环境清理的标准流程。


一、排查前的准备工作

在开始删除任何文件前,先做两件事:

# 记录当前磁盘状态,以便事后回溯$ df -h > /tmp/disk_before_$(date +%s).txt$ df -i >> /tmp/disk_before_$(date +%s).txt# 确认挂载点对应的业务$ mount | grep <挂载点>$ ls -la <挂载点> | head

如果有多块数据盘(如 /data、/var/log 独立分区),先确认哪个分区满了,再针对性排查。


二、8 个命令逐一详解

命令 1:df -h —— 查看各挂载点使用率

$ df -hFilesystem      Size  Used Avail Use% Mounted on/dev/vda1        40G   38G   16M  100% //dev/vdb1       200G  120G   80G   60% /data

关键检查点:

  • **Use% = 100%**:该分区空间已满,需要立即处理
  • **Use% = 90~99%**:预警范围,需要排查哪些目录在持续增长
  • 关注 Avail 列而非 Used 列:Avail 是实际可用空间(非 root 用户可用),Used 可能包含预留块(默认 5%)

预留块含义:mkfs.ext4 默认保留 5% 给 root 用户,所以非 root 用户看到 Avail 会比预期少。数据盘可以用 tune2fs -m 1 /dev/vdb1 调低到 1%。

命令 2:df -i —— 检查 inode 是否耗尽

$ df -iFilesystem      Inodes  IUsed   IFree IUse% Mounted on/dev/vda1      256000 255900     100  100% /

典型陷阱:df -h 显示还有空间,但系统报 No space left on device,此时 df -i 很可能显示 IUsed = 100%。

inode 耗尽的原因是小文件太多——每个文件(不管大小 1 字节还是 1GB)都需要一个 inode。一个 40GB 的分区如果满了 255 万个小文件,inode 就会耗尽,实际空间可能只用了 10%。

快速定位小文件目录:

# 统计各目录的文件数量(递归深度 1)$ for dir in /*/; doecho -n "$dir: "; find "$dir" -xdev -type f 2>/dev/null | wc -l; done# -xdev 限制在同一文件系统,避免统计到挂载点内# 更精确的方式——逐级排查$ find / -xdev -type f | awk -F/ '{$NF=""; print $0}' | sort | uniq -c | sort -rn | head -10

命令 3:du -sh —— 逐层定位大目录

# 查看根目录下各一级目录的大小$ du -h --max-depth=1 / | sort -rh | head -106.2G    /usr4.1G    /var2.8G    /opt1.5G    /home1.2G    /root...# 发现 /var 大后,再深入$ du -h --max-depth=1 /var/ | sort -rh | head -103.5G    /var/log500M    /var/lib...# 直到定位到具体目录$ du -sh /var/log/nginx/1.2G    /var/log/nginx/

du 递归统计所有子目录,大目录下逐层深入很快就能找到"罪魁祸首"。

常用变体:

# 只看当前目录下各子目录大小,不递归更深$ du -sh */ .[!.]*/# 用 time 限制范围(只看修改时间早于 N 天的文件)# 统计 30 天前的日志总量$ find /var/log -name "*.log" -mtime +30 -type f -exec du -ch {} + | tail -1

命令 4:lsof | grep deleted —— 查找已删除但未释放的文件

这是最容易被忽略但实际非常常见的场景。当一个文件被 rm 删除后,如果有进程仍然持有这个文件的句柄,磁盘空间不会立即释放。

# 检查所有已删除但仍有进程引用的文件$ sudo lsof | grep deletedCOMMAND    PID   USER   FD   TYPE DEVICE SIZE/OFF    NODE NAMEjava      3456   root   23w   REG  202,1 2147483648  12345 /var/log/app/access.log (deleted)nginx     2321   www    5w    REG  202,1 1073741824  23456 /var/log/nginx/access.log (deleted)

关键信息解读:

  • SIZE/OFF 列:文件的实际大小(单位字节),这里是 2GB 和 1GB
  • NAME 列末尾的 (deleted) 标记说明文件已被删除但句柄未释放
  • FD(File Descriptor)列:23w 表示第 23 号文件描述符,打开方式为写入

解决方法(按推荐顺序):

  1. 安全方式——通知进程重载文件句柄(大多数日志框架支持):

    $ kill -USR1 <PID>     # Java / log4j / syslog-ng$ nginx -s reopen      # Nginx$ systemctl restart rsyslog
  2. 强制方式——如果进程不响应 USR1 信号或不是日志类文件:

    # 确认进程是否可以重启$ systemctl restart <service># 或直接 kill 进程(确认不影响业务后)$ kill -9 <PID>
  3. 临时释放——清空文件内容而非删除文件,不依赖信号:

    $ : > /proc/<PID>/fd/<FD_NUMBER>

    这不会关闭进程的文件描述符,而是将文件内容截断为 0,立即释放磁盘空间。适用于不能重启进程的生产环境。

为什么会出现这种情况:

最常见的场景是 logrotate。很多默认的 logrotate 配置使用 create 指令,流程是:mv access.log access.log.1 -> create access.log(新建空文件)。新文件有了新 inode,但旧文件的句柄仍然被进程持有,旧文件的磁盘空间一直占用直到进程重启。

正确的做法是 logrotate 配置 copytruncate:

/var/log/nginx/*.log {    daily    rotate 30    copytruncate    # 复制内容后截断原文件,不改变 inode    compress    delaycompress    missingok    notifempty}

命令 5:find 查找大文件

# 查找根目录下大于 100MB 的文件$ find / -xdev -type f -size +100M -exec ls -lh {} \; 2>/dev/null | sort -k5 -rh | head -20# 查找特定目录下大于 1GB 的文件$ find /var/log -type f -size +1G -exec ls -lh {} \; 2>/dev/null# 查找最近 7 天内没有修改过的大文件(适合清理历史归档)$ find /data/archive -type f -size +500M -mtime +7 -exec ls -lh {} \; 2>/dev/null

参数解释:

  • -xdev:限制在同一文件系统内,不搜索挂载的其他分区
  • -size +100M:大于 100MB 的文件(单位:k/M/G)
  • -exec ls -lh {} \;:对每个结果执行 ls
  • sort -k5 -rh:按第 5 列(文件大小)反向排序
  • 2>/dev/null:过滤权限不足的错误信息

命令 6:ncdu —— 交互式磁盘分析

ncdu(NCurses Disk Usage)是 du 的交互式替代品,对逐层排查大目录效率更高:

# 安装$ apt install ncdu      # Debian/Ubuntu$ yum install ncdu      # CentOS/RHEL# 使用$ ncdu /

操作方式:

  • 方向键上下移动
  • Enter 进入目录
  • d 删除选中的文件/目录
  • q 退出

ncdu 扫描速度比 du 快(尤其在大目录下),而且可以实时看到各目录大小排序,不需要反复敲命令。

命令 7:iostat -xz —— 分析磁盘 IO 性能

当磁盘使用率没有 100% 但业务响应仍然很慢时,可能是 IO 性能达到上限而非空间不足:

$ iostat -xz 1 5Linux 5.15.0-91-generic ... 08/15/2025  _x86_64_ (32 CPU)Device   r/s     w/s    rkB/s    wkB/s  rrqm/s  wrqm/s  %rrqm  %wrqm r_await w_await aqu-sz  %utilvda    3200.0  4500.0 128000.0 360000.0    0.0     0.0   0.00   0.00   15.20  92.30  256.0  99.8

关键指标:

指标
含义
HDD 警戒线
SSD 警戒线
%util
磁盘忙碌百分比
> 80%
对 SSD 参考价值有限
r_await / w_await
读写平均延迟
> 20ms
> 2ms
aqu-sz
平均队列长度
> 1
> N(并行度)
r/s + w/s
IOPS
机械盘 ~200
取决于 SSD 型号

对 NVMe SSD 的说明:%util 对多队列设备(NVMe)不准确,可能显示 100% 但仍能处理更多 IO。更可靠的指标是 aqu-sz(平均队列长度)和 r_await / w_await(IO 延迟)。

命令 8:journalctl --disk-usage —— 检查 systemd 日志占用

systemd-journald 的日志在不加限制的情况下可能占用数 GB 磁盘空间:

# 查看 journal 日志占用的磁盘空间$ journalctl --disk-usageArchived and active journals use 3.8G.# 清理 7 天前的日志$ journalctl --vacuum-time=7d# 限制 journal 最大体积为 500MB$ journalctl --vacuum-size=500M# 永久限制(编辑配置文件)$ cat /etc/systemd/journald.conf[Journal]SystemMaxUse=500MMaxFileSec=7day

修改配置后重启 systemd-journald:

$ systemctl restart systemd-journald

三、按场景组合使用 8 个命令

场景 A:空间占比高,du 能正常定位

执行顺序:df -h → du 逐级定位 → find 查找大文件 → ncdu 交互式确认

$ df -h                     # 定位满的分区$ du -h --max-depth=1 /     # 逐级深入$ find /path -type f -size +500M -exec ls -lh {} \;$ ncdu /path                # 交互式确认

场景 B:df 显示满,但 du 统计不到大文件

执行顺序:df -h → sudo lsof | grep deleted → 确认后释放

$ sudo lsof | grep deleted | head -5# 找到最大的(deleted)文件$ sudo lsof | grep deleted | awk '{print $7, $NF, $2}' | sort -rn | head -10# 对 PID 发送信号或重启服务

场景 C:No space left on device 但 df -h 有空间

执行顺序:df -i → find 统计文件数 → 清理小文件

$ df -i                      # 确认 inode 满$ find /data -xdev -type f | wc -l              # 统计文件总数$ find /data -xdev -type f -mtime +90 -delete   # 清理 90 天前的旧文件

场景 D:磁盘性能差但空间充足

执行顺序:iostat -xz → iotop -o → pidstat -d

$ iostat -xz 1 5             # 确认 %util 高、await 高$ iotop -o                   # 定位 IO 密集进程$ pidstat -d 1 5             # 按进程统计 IO

四、生产环境标准清理流程

不要在发现磁盘满时直接上手删文件,遵循以下流程:

记录现场 → 定位大目录 → 确认业务影响 → 备份 → 清理 → 验证

Step 1:记录现场

$ df -h > /tmp/disk_clean_$(date +%s).before$ du -h --max-depth=1 /var/log | sort -rh > /tmp/disk_clean_$(date +%s).logdir

Step 2:定位并确认

# 找到大文件后确认其业务归属$ ls -la /var/log/nginx/access.log.20250815.gz# 与业务方确认是否可以清理

Step 3:备份后再清理

对于不确定的日志/数据文件,压缩备份到其他分区或存储后再清理:

# 备份到其他分区$ gzip -c /var/log/nginx/access.log.20250815.gz > /backup/nginx/access.log.20250815.gz# 确认备份成功后删除$ rm /var/log/nginx/access.log.20250815.gz

Step 4:验证空间是否释放

$ df -h <挂载点>$ df -i <挂载点>   # 如果之前 inode 也高的话

Step 5:配置长期策略

根据排查结果配置日志轮转或周期性清理:

# logrotate 示例:按大小轮转/var/log/nginx/*.log {    size 500M    rotate 14    copytruncate    compress    missingok    notifempty}# crontab 定期清理任务0 3 * * 0 find /data/tmp -type f -mtime +30 -delete

五、预防措施

  1. 日志轮转必须配置——没有 logrotate 的服务器迟早会磁盘满。检查是否存在未被 logrotate 覆盖的日志文件。
  2. 监控与告警:不要等到 100% 再告警,分三级告警:
    • Warning: 使用率 > 80%
    • Critical: 使用率 > 90%
    • Emergency: 使用率 > 95%
  3. 独立分区:将 /var/log、/data 等容易写满的目录独立分区,避免写满根分区导致系统无法正常启动。
  4. 定期巡检:每周自动扫描各分区使用率和大文件增长趋势。

六、注意事项

  1. 不要在根分区满时重启服务器——根分区没有剩余空间可能导致重启后部分服务无法正常启动。
  2. %util 100% 不等于磁盘损坏——可能是正常的高负载,先确认 aqu-sz 和 await 是否异常。
  3. df 和 du 的统计差异:正常情况下差异在 5% 以内。如果差异显著,优先检查 lsof | grep deleted。
  4. 挂载点覆盖:如果在非空目录上挂载新分区,原目录下的文件会被隐藏但仍占用空间。排查时注意 mount 输出。
  5. **生产环境慎用 rm -rf**——可能误删正在写入的日志文件,导致句柄未释放反而无法释放空间。建议优先用 echo "" > file 或 truncate -s 0 file。

七、总结

磁盘满排查的核心路径:

  1. 先看 df -h 确认空间耗尽的分区和挂载点
  2. 再看 df -i 排除 inode 耗尽的情况
  3. df 满但 du 找不到 → 执行 lsof | grep deleted
  4. 逐层 du 定位最大目录
  5. find 和 ncdu 查找具体大文件
  6. 检查 journalctl 系统日志占用
  7. 配合 iostat 排除 IO 性能问题

磁盘满的最佳应对策略不是"发现满了再删",而是通过 logrotate、分区规划、监控预警建立一个防止磁盘写满的体系。磁盘满了之后的每一次删除,都应该有记录、有备份、有确认、有总结。

最新文章

随机文章