当前位置:首页>Linux>Linux 磁盘爆满怎么办?du/df/lsof/find 排查与清理实战

Linux 磁盘爆满怎么办?du/df/lsof/find 排查与清理实战

  • 2026-10-10 13:23:55
Linux 磁盘爆满怎么办?du/df/lsof/find 排查与清理实战

Linux 磁盘爆满怎么办?du/df/lsof/find 排查与清理实战

「零壹运维 · 零到壹,永不宕」

凌晨三点告警响起:磁盘使用率 100%,服务写入失败。面对这种情况,你是手忙脚乱还是有条不紊?本文带你掌握磁盘爆满的完整排查与清理方法,让你下次遇到这类问题时,10 分钟内从容搞定。


一、为什么磁盘会突然爆满?

磁盘爆满通常有几类元凶:

类型
常见原因
日志失控
应用日志无限增长、未配置 logrotate
核心转储
程序崩溃产生 core dump 文件
临时文件堆积
/tmp 未清理、容器镜像层残留
数据库膨胀
MySQL binlog 未清理、ES 索引堆积
已删除文件未释放
进程还持有已删除文件的句柄
inode 耗尽
小文件过多,inode 用完但磁盘空间看起来还有

⚠️ 注意:磁盘满 ≠ 一定能 df 看到空间不足,inode 耗尽同样会导致写入失败!


二、工具速查表

工具
作用
核心场景
df
查看文件系统磁盘使用量
第一步:定位哪个分区满了
du
统计目录/文件占用空间
第二步:找出大目录/大文件
find
按条件搜索文件
找大文件、老文件、特定类型文件
lsof
列出打开的文件
找已删除但未释放的文件句柄

三、df — 磁盘使用全览

基本用法

# 查看所有挂载点使用情况(人类可读格式)
df -h

# 查看 inode 使用情况(关键!)
df -i

# 指定目录所在分区
df -h /var/log

典型输出解读

Filesystem      Size  Used Avail Use%  Mounted on
/dev/sda1        50G   49G  500M  99%  /
tmpfs           7.8G     0  7.8G   0%  /dev/shm
/dev/sdb1       200G   50G  150G  25%  /data

重点关注:Use% 达到 95% 以上就要警惕,100% 服务直接写入失败。

inode 耗尽排查

df -i
# 输出示例:
# Filesystem     Inodes  IUsed  IFree IUse% Mounted on
# /dev/sda1      3276800 3276800     0  100% /

# inode 100% 但磁盘空间还有 → 小文件过多
# 找出哪个目录 inode 最多
for i in /*; doecho$i; find $i -xdev | wc -l; done | paste - - | sort -k2 -rn | head -10

四、du — 揪出空间元凶

核心参数

参数
说明
-h
人类可读(KB/MB/GB)
-s
只显示总计(不展开子目录)
-a
显示所有文件(包括文件本身)
--max-depth=N
限制展开深度
--exclude=PATTERN
排除匹配的目录

标准排查流程

# Step 1:从根目录开始,找最大的一级目录
du -sh /* 2>/dev/null | sort -rh | head -10

# Step 2:进入最大目录,继续下钻
du -sh /var/* 2>/dev/null | sort -rh | head -10

# Step 3:定位到具体子目录
du -sh /var/log/* 2>/dev/null | sort -rh | head -10

# Step 4:找出单个大文件
du -ah /var/log/ 2>/dev/null | sort -rh | head -20

快速找大目录(一行命令)

# 找出超过 1GB 的目录
du -h --max-depth=3 / 2>/dev/null | grep -E '^[0-9.]+G' | sort -rh | head -20

# 找出当前目录下各子目录大小排行
du -sh ./* | sort -rh | head -20

五、find — 精准定位问题文件

按文件大小查找

# 找出大于 500MB 的文件
find / -xdev -size +500M -type f 2>/dev/null

# 找出大于 100MB 的文件,显示详细信息
find / -xdev -size +100M -type f -exec ls -lh {} \; 2>/dev/null | sort -k5 -rh

# 找出大于 1GB 的文件
find / -xdev -size +1G -type f 2>/dev/null | xargs ls -lh 2>/dev/null

按时间查找

# 找出 30 天未访问的日志文件
find /var/log -name "*.log" -atime +30 -type f

# 找出 7 天内修改过的大文件(>100MB)
find / -xdev -mtime -7 -size +100M -type f 2>/dev/null

# 找出 60 天前的备份文件
find /backup -name "*.tar.gz" -mtime +60 -type f

按文件类型查找

# 找出所有 core dump 文件
find / -name "core" -o -name "core.*" 2>/dev/null | grep -v proc

# 找出所有 .log 文件并统计总大小
find /var/log -name "*.log" -type f | xargs du -sh 2>/dev/null | sort -rh

# 找出所有压缩包
find / -xdev -name "*.tar.gz" -o -name "*.zip" -o -name "*.bak" 2>/dev/null

# 找出空文件(占用 inode 但不占空间)
find /tmp -type f -empty 2>/dev/null | wc -l

查找 + 删除(高危操作,先确认再删)

# 先列出,确认无误再删
find /var/log -name "*.log.gz" -mtime +30 -type f

# 确认后删除(-delete 比 rm 更安全)
find /var/log -name "*.log.gz" -mtime +30 -type f -delete

# 或者移动到备份目录
find /var/log -name "*.log.gz" -mtime +30 -type f -exec mv {} /backup/ \;

六、lsof — 找出已删除但未释放的文件

这个场景非常常见!

# 删除了大文件,df 显示空间没有释放
rm -f /var/log/app.log   # 删了
df -h                     # 空间还没变!

原因:文件被删除,但进程仍然持有文件描述符,内核不会真正释放磁盘空间,直到所有句柄都关闭。

查找已删除但未释放的文件

# 找出所有已删除但仍被进程持有的文件
lsof | grep deleted

# 只看大文件(按大小排序)
lsof | grep deleted | awk '{print $7, $1, $2}' | sort -rn | head -20

# 更完整的格式(进程名、PID、文件大小、文件路径)
lsof +L1 | awk 'NR>1 {printf "%s\t%s\t%s\t%s\n", $7, $1, $2, $NF}' | sort -rn | head -20

输出解读

COMMAND   PID  USER  FD  TYPE  SIZE/OFF  NLINK  NAME
nginx     1234 root  10r  REG   500M       0    /var/log/nginx/access.log (deleted)
java      5678 app   20w  REG   2.1G       0    /tmp/app-temp.log (deleted)
  • NLINK=0:文件已从目录中删除,但还被持有
  • SIZE:实际占用磁盘的大小

释放空间的方法

# 方法一:重启持有句柄的进程(最干净)
systemctl restart nginx

# 方法二:清空文件内容(不中断进程)
# 找到进程 PID 和文件描述符 FD
lsof | grep deleted | grep nginx
# 清空(通过 /proc 访问)
> /proc/1234/fd/10
# 或者
cat /dev/null > /proc/1234/fd/10

# 方法三:直接 kill 进程(最后手段)
kill -9 1234

七、完整排查 SOP(标准操作流程)

磁盘告警触发
     │
     ▼
Step 1: df -h           ── 哪个分区满了?
     │
     ├── Use% 100%       ── 进入 Step 2
     │
     └── Use% 正常 ──── df -i 检查 inode
                         inode 100%?→ 小文件清理方案
     │
     ▼
Step 2: du -sh /* | sort -rh    ── 找最大一级目录
     │
     ▼
Step 3: 逐层下钻 du -sh /xxx/*  ── 定位到具体目录
     │
     ├── 日志目录占大?   ── Step 4a: 日志清理
     ├── tmp 目录占大?   ── Step 4b: 临时文件清理
     ├── 容器相关目录?   ── Step 4c: Docker 清理
     └── 其他大文件?     ── Step 4d: find + 确认删除
     │
     ▼
Step 4: lsof | grep deleted     ── 有已删除未释放?
     │
     ├── 有 → 重启进程或清空 fd
     └── 无 → 直接删除大文件
     │
     ▼
Step 5: df -h 验证空间释放

八、常见场景实战

场景一:/var/log 日志目录爆满

# 1. 确认问题
du -sh /var/log/* | sort -rh | head -10

# 2. 找出最大的日志文件
find /var/log -type f -name "*.log" | xargs ls -lh | sort -k5 -rh | head -10

# 3. 安全清空(不删文件,避免破坏 logrotate 配置)
> /var/log/app/application.log

# 4. 删除已归档的压缩日志
find /var/log -name "*.gz" -mtime +7 -delete
find /var/log -name "*.log.*" -mtime +7 -delete

# 5. 配置 logrotate 防止再次爆满
cat /etc/logrotate.d/myapp

logrotate 配置示例:

/var/log/myapp/*.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    copytruncate
    size 100M
}

场景二:Docker 磁盘占用过大

# 查看 Docker 整体占用
docker system df

# 详细信息
docker system df -v

# 清理停止的容器、悬空镜像、未使用网络
docker system prune

# 同时清理未使用的数据卷(⚠️ 谨慎!)
docker system prune --volumes

# 只清理悬空镜像
docker image prune

# 清理 30 天前的镜像
docker image prune -a --filter "until=720h"

场景三:MySQL binlog 堆积

# 查看 binlog 占用
du -sh /var/lib/mysql/mysql-bin.* | sort -rh | head -10

# 查看当前 binlog 列表
mysql -e "SHOW BINARY LOGS;"

# 删除指定日志前的所有 binlog
mysql -e "PURGE BINARY LOGS TO 'mysql-bin.000100';"

# 删除指定时间前的 binlog
mysql -e "PURGE BINARY LOGS BEFORE '2026-03-01 00:00:00';"

# 根本解决:设置 binlog 自动过期(my.cnf)
# expire_logs_days = 7
# 或(MySQL 8.0+)
# binlog_expire_logs_seconds = 604800

场景四:/tmp 临时文件堆积

# 查看 /tmp 占用
du -sh /tmp/* 2>/dev/null | sort -rh | head -20

# 找出 7 天前的临时文件
find /tmp -mtime +7 -type f 2>/dev/null

# 安全清理(排除当前进程使用的文件)
find /tmp -mtime +1 -type f ! -newer /proc/1 -delete 2>/dev/null

# 配置系统自动清理 /tmp(systemd-tmpfiles)
cat /usr/lib/tmpfiles.d/tmp.conf
# 默认配置:/tmp 下 10 天未访问的文件自动清理

场景五:core dump 文件堆积

# 找出所有 core dump
find / -name "core" -o -name "core.[0-9]*" 2>/dev/null | grep -v /proc | grep -v /sys

# 查看 core dump 配置
ulimit -c
cat /proc/sys/kernel/core_pattern

# 临时禁用 core dump
ulimit -c 0

# 永久禁用(写入 /etc/security/limits.conf)
echo"* soft core 0" >> /etc/security/limits.conf
echo"* hard core 0" >> /etc/security/limits.conf

# 如果需要保留 core dump,设置存放目录和大小限制
echo"/var/core/core-%e-%p-%t" > /proc/sys/kernel/core_pattern

九、inode 耗尽专项处理

inode 耗尽通常是小文件太多(如 session 文件、缓存文件、邮件队列):

# 确认 inode 耗尽
df -i | grep 100%

# 找出哪个目录小文件最多
for dir in /var /tmp /home /opt; do
echo"=== $dir ==="
  find "$dir" -xdev -type f 2>/dev/null | wc -l
done

# 更快的方式:统计各目录文件数
find / -xdev -type f 2>/dev/null | awk -F'/''{print "/"$2}' | sort | uniq -c | sort -rn | head -20

# 常见元凶:PHP session 文件
ls /var/lib/php/sessions/ | wc -l
# 清理超过 1 天的 session
find /var/lib/php/sessions/ -mtime +1 -delete

# Postfix 邮件队列
find /var/spool/postfix/ -type f | wc -l
postsuper -d ALL   # 清空邮件队列(谨慎!)

十、预防胜于治疗:监控与告警

磁盘监控 Shell 脚本

#!/bin/bash
# disk_monitor.sh - 磁盘使用率监控

THRESHOLD=85
ALERT_EMAIL="ops@example.com"

df -h | grep -vE '^Filesystem|tmpfs|cdrom' | awk '{print $5 " " $6}' | whileread output; do
  usage=$(echo$output | awk '{print $1}' | tr -d '%')
  partition=$(echo$output | awk '{print $2}')

if [ "$usage" -ge "$THRESHOLD" ]; then
echo"警告:$partition 使用率 ${usage}%" | mail -s "磁盘告警"$ALERT_EMAIL
echo"$(date): 磁盘告警 - $partition 使用率 ${usage}%" >> /var/log/disk_monitor.log
fi
done

Prometheus 告警规则

groups:
-name:disk
rules:
-alert:DiskSpaceLow
expr:(node_filesystem_avail_bytes{mountpoint="/"}/node_filesystem_size_bytes{mountpoint="/"})<0.15
for:5m
labels:
severity:warning
annotations:
summary:"磁盘空间不足 ({{ $labels.instance }})"
description:"根分区剩余空间低于 15%,当前值: {{ $value | humanizePercentage }}"

-alert:DiskSpaceCritical
expr:(node_filesystem_avail_bytes{mountpoint="/"}/node_filesystem_size_bytes{mountpoint="/"})<0.05
for:1m
labels:
severity:critical
annotations:
summary:"磁盘空间严重不足!({{ $labels.instance }})"
description:"根分区剩余空间低于 5%,请立即处理!"

-alert:InodeUsageHigh
expr:(node_filesystem_files_free/node_filesystem_files)<0.10
for:5m
labels:
severity:warning
annotations:
summary:"inode 使用率过高 ({{ $labels.instance }})"

十一、命令速查卡

# ===== df =====
df -h                          # 磁盘使用概览
df -i                          # inode 使用情况
df -h /var                     # 查看指定目录所在分区

# ===== du =====
du -sh /*                      # 一级目录大小排行
du -sh /var/* | sort -rh       # 按大小排序
du -sh --max-depth=2 /         # 限制递归深度

# ===== find =====
find / -xdev -size +500M -type f              # 找大于 500MB 的文件
find /var/log -name "*.log" -mtime +30        # 找 30 天前的日志
find / -name "core*" 2>/dev/null              # 找 core dump

# ===== lsof =====
lsof | grep deleted                           # 已删除但未释放的文件
lsof +L1 | sort -k7 -rn | head              # 按大小排序

# ===== 清理类 =====
docker system prune                           # 清理 Docker 垃圾
journalctl --vacuum-size=500M                 # 限制 journald 日志大小
journalctl --vacuum-time=7d                   # 只保留 7 天日志
> /var/log/app.log                            # 清空日志文件(保留文件)
truncate -s 0 /var/log/app.log               # 同上,更规范

十二、最佳实践

  1. 建立监控:磁盘使用率 > 80% 告警,> 90% 紧急告警,inode 同样监控
  2. 提前规划分区:/var、/tmp、/home 建议独立分区,避免影响根分区
  3. 配置 logrotate:所有应用日志必须配置,设置 size 和 rotate 双重保障
  4. 定期清理 Docker:docker system prune 加入 crontab,每周执行一次
  5. 先看再删:所有 find ... -delete 操作,先去掉 -delete 确认文件列表
  6. lsof 必查:空间未释放时,第一时间执行 lsof | grep deleted
  7. inode 不能忽视:磁盘告警配置要同时监控 inode,小文件环境尤其重要

小结

磁盘爆满的排查四步走:

df -h 定位分区 → du 找大目录 → find 定位文件 → lsof 查未释放

记住:删之前先看,看完再删;重启前先清空,清空后再重启。 生产环境没有后悔药,谨慎操作是第一准则。


「零壹运维 · 零到壹,永不宕」

最新文章

随机文章