Linux 磁盘爆满怎么办?du/df/lsof/find 排查与清理实战
「零壹运维 · 零到壹,永不宕」
凌晨三点告警响起:磁盘使用率 100%,服务写入失败。面对这种情况,你是手忙脚乱还是有条不紊?本文带你掌握磁盘爆满的完整排查与清理方法,让你下次遇到这类问题时,10 分钟内从容搞定。
一、为什么磁盘会突然爆满?
磁盘爆满通常有几类元凶:
| |
|---|
| 日志失控 | |
| 核心转储 | |
| 临时文件堆积 | |
| 数据库膨胀 | |
| 已删除文件未释放 | |
| inode 耗尽 | |
⚠️ 注意:磁盘满 ≠ 一定能 df 看到空间不足,inode 耗尽同样会导致写入失败!
二、工具速查表
三、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 | |
-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)
释放空间的方法
# 方法一:重启持有句柄的进程(最干净)
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 # 同上,更规范
十二、最佳实践
- 建立监控:磁盘使用率 > 80% 告警,> 90% 紧急告警,inode 同样监控
- 提前规划分区:
/var、/tmp、/home 建议独立分区,避免影响根分区 - 配置 logrotate:所有应用日志必须配置,设置
size 和 rotate 双重保障 - 定期清理 Docker:
docker system prune 加入 crontab,每周执行一次 - 先看再删:所有
find ... -delete 操作,先去掉 -delete 确认文件列表 - lsof 必查:空间未释放时,第一时间执行
lsof | grep deleted - inode 不能忽视:磁盘告警配置要同时监控 inode,小文件环境尤其重要
小结
磁盘爆满的排查四步走:
df -h 定位分区 → du 找大目录 → find 定位文件 → lsof 查未释放
记住:删之前先看,看完再删;重启前先清空,清空后再重启。 生产环境没有后悔药,谨慎操作是第一准则。
「零壹运维 · 零到壹,永不宕」