磁盘空间不足处理
问题
收到告警:服务器磁盘使用率超过 90%,如何快速定位并处理?
答案
排查流程
Step 1:确认哪个分区满了
# 查看各分区使用率
df -h
# Filesystem Size Used Avail Use% Mounted on
# /dev/sda1 50G 47G 2.3G 96% /
# /dev/sdb1 200G 80G 110G 42% /data
# 查看 inode 使用率(小文件过多也会导致"磁盘满")
df -i
inode 耗尽
df -h 显示有空间但无法创建文件时,检查 df -i。大量小文件(如 session 文件、缓存碎片)可能耗尽 inode。
Step 2:定位大目录和大文件
# 找到根目录下最大的目录(一级一级往下找)
du -sh /* 2>/dev/null | sort -rh | head -10
du -sh /var/* 2>/dev/null | sort -rh | head -10
du -sh /var/log/* 2>/dev/null | sort -rh | head -10
# 直接找大文件(> 100MB)
find / -type f -size +100M -exec ls -lh {} \; 2>/dev/null | sort -k5 -rh | head -20
# 找 30 天未访问的大文件
find /data -type f -size +100M -atime +30 -exec ls -lh {} \;
Step 3:处理已删除但未释放的文件
# 进程持有已删除文件的句柄,空间不会释放
# 查找这类文件
lsof +L1 | grep deleted
# 示例输出:
# nginx 1234 root 5w REG 8,1 5368709120 0 /var/log/nginx/access.log (deleted)
# 说明:nginx 还在写已被删除的日志文件,占用 5GB
# 解决方案 1:重启进程释放句柄
systemctl restart nginx
# 解决方案 2:不重启,清空文件内容(推荐)
# 通过 /proc 截断文件
: > /proc/1234/fd/5
不要直接 rm 正在写入的日志
rm 只是删除目录项,进程仍然持有文件描述符。正确做法是用 > file 截断文件而不是删除:
# ❌ 错误:空间不会释放(进程还在写)
rm /var/log/app/huge.log
# ✅ 正确:截断文件,立即释放空间
> /var/log/app/huge.log
# 或者
truncate -s 0 /var/log/app/huge.log
Step 4:紧急清理手段
# 1. 清理系统日志
journalctl --vacuum-size=500M # 限制 journal 日志为 500M
journalctl --vacuum-time=7d # 只保留 7 天
# 2. 清理 yum/apt 缓存
yum clean all # CentOS/RHEL
apt-get clean # Ubuntu/Debian
# 3. 清理 Docker(Docker 是磁盘杀手)
docker system prune -a --volumes # 清理无用镜像、容器、卷
docker system df # 查看 Docker 占用
# 4. 清理临时文件
find /tmp -type f -atime +7 -delete
find /var/tmp -type f -atime +30 -delete
# 5. 清理旧内核(Ubuntu)
apt autoremove --purge
长期预防方案
| 方案 | 说明 |
|---|---|
| logrotate | 配置日志自动轮转和清理 |
| 监控告警 | 80% 预警、90% 紧急告警 |
| 磁盘增长趋势 | 监控磁盘周增长率,提前扩容 |
| Docker 清理定时任务 | cron 定期执行 docker system prune |
| 日志存储分离 | 日志独立分区,不影响系统盘 |
常见面试问题
Q1: 磁盘满了导致数据库不可用,如何紧急处理?
答案:
-
立即止血:
- 清理 binlog:
PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 1 DAY) - 清理慢查询日志:
> /var/log/mysql/slow.log && mysqladmin flush-logs - 清理临时文件:
ls -la /tmp/mysql*并删除旧的临时文件
- 清理 binlog:
-
如果数据库无法执行 SQL(磁盘完全满):
# 先手动删除一些文件腾出空间(如旧的 binlog)
ls -lt /var/lib/mysql/mysql-bin.* | tail -5
rm /var/lib/mysql/mysql-bin.000001
# 然后进入 MySQL 执行 RESET MASTER 或 PURGE -
长期方案:binlog 过期时间设置、独立分区、容量监控