跳到主要内容

磁盘空间不足处理

问题

收到告警:服务器磁盘使用率超过 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: 磁盘满了导致数据库不可用,如何紧急处理?

答案

  1. 立即止血

    • 清理 binlog:PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 1 DAY)
    • 清理慢查询日志:> /var/log/mysql/slow.log && mysqladmin flush-logs
    • 清理临时文件:ls -la /tmp/mysql* 并删除旧的临时文件
  2. 如果数据库无法执行 SQL(磁盘完全满):

    # 先手动删除一些文件腾出空间(如旧的 binlog)
    ls -lt /var/lib/mysql/mysql-bin.* | tail -5
    rm /var/lib/mysql/mysql-bin.000001

    # 然后进入 MySQL 执行 RESET MASTER 或 PURGE
  3. 长期方案:binlog 过期时间设置、独立分区、容量监控

相关链接