服务器磁盘爆满应急处理:df 与 du 定位大文件并安全清理

服务器磁盘爆满应急处理封面图

凌晨收到磁盘告警、网站突然打不开、数据库写入失败——服务器磁盘爆满是运维中最紧急的故障之一。它不仅让网站瘫痪,还会让 MySQL(流行的开源数据库)中断写入、日志无法落盘,恢复窗口拖得越久风险越大。这篇指南会教你如何用 df 与 du 两条命令快速定位空间去向,识别日志膨胀、Docker 镜像堆积、binlog 失控三大常见元凶,并安全清理而不误伤业务数据。无论你用的是独立服务器还是 VPS虚拟专用服务器),这套排查路径都能直接照做。

一、磁盘爆满的典型信号与先止血

处理故障前先识别症状。磁盘快满或已满时,服务器会给出这些信号:df 输出中根分区或数据分区使用率超过 90%;网站返回 500 或 502,因为 PHP 已无法写临时文件;MySQL 日志出现 “Disk full”;systemctl 启动服务失败;极端情况下连 SSH 登录都会因为无法写会话文件而卡住。

如果服务已经中断,第一动作是”止血”而不是慢慢分析。先清空占空间最明显的一类文件——系统日志:

journalctl --disk-usage
journalctl --vacuum-size=200M

第一条查看 systemd journal 日志占用,第二条把 journal 压缩到 200MB。journal 是磁盘爆满案例中占比极高的元凶:默认配置下它会自动轮转,但如果某个服务疯狂刷错误日志(比如数据库连接失败每秒重试),几十 GB 的日志几个小时就能堆出来。执行完这两条命令,多数紧急场景能先腾出几个 GB,让服务恢复写入,再从容进入定位阶段。更多日常巡检思路可参考 服务器优化专栏

二、df 与 du:定位空间去向的两把尺子

止血之后要回答的问题是:空间到底去哪了?df 与 du 分工不同:df 看”每个文件系统还剩多少”,du 看”某个目录占了多少”。排查从 df 开始:

df -hT

-h 以人类可读的 GB/MB 显示,-T 附加文件系统类型。重点看两个数字:Use% 超过 90% 的分区,以及它挂载在哪个目录(通常是 //var)。同时留意 inode:

df -i

有一种特殊故障是”空间没用完但磁盘报满”——海量小文件(比如邮件队列、会话文件)把 inode 耗尽,此时 df -h 显示正常,df -i 却接近 100%。两种口径都要看,才能不漏判。

确认爆满的分区后,用 du 从根目录向下钻。/proc/sys/dev 是内核虚拟目录,必须排除:

du -h --max-depth=1 / 2>/dev/null | sort -hr | head -15

这条命令列出根下每个一级目录的实际占用并按大小排序。--max-depth=1 控制深度避免输出爆炸,2>/dev/null 丢弃权限不足的报错。看到可疑目录(常见是 /var/home/usr/opt)后,逐层下钻重复执行,比如 du -h --max-depth=1 /var | sort -hr | head,两三层之内就能锁定大目录。最后找具体大文件:

find /var -type f -size +500M -exec ls -lh {} \; 2>/dev/null

超过 500MB 的文件会逐一列出。把这套”df 定位分区 → du 定位目录 → find 定位文件”的路径走完,空间去向自然水落石出。

df 与 du 逐层定位磁盘占用示意图

三、三大常见元凶与对应的安全清理方法

定位到具体文件后,不能见大就删。以下三类是生产环境磁盘爆满的常客,各自的清理方式差别很大。

元凶一:日志文件失控。 /var/log 是 du 排查中的高频目的地。典型模式是某服务异常后每秒刷日志,日志文件膨胀到几十 GB。安全清理分两步:先用 journalctl --vacuum-size=200M 处理 journal;对普通日志先确认写入方已停止或已轮转再删除。这里有个经典陷阱:用 rm 删除一个仍被进程打开的日志文件,df 显示的空间并不会回来——文件句柄还在进程手里,inode 未释放。正确姿势是清空而不是删除:

> /var/log/nginx/error.log

> 把文件截断为 0 字节,进程的写入位置重置,磁盘空间立即释放。清空后记得检查 lsof +L1,列出所有”已删除但仍占用空间”的文件,若还有残留,重启对应服务即可彻底回收。

元凶二:Docker 镜像与容器垃圾。 跑过几次镜像构建或反复 docker pull 之后,/var/lib/docker 很容易吃掉几十 GB。安全清理一条命令:

docker system prune -af

它会删除所有未使用的镜像、停止的容器和未引用的网络。注意 -a 会把当前没有容器引用的镜像全部清掉,下次启动要重新拉取。保守做法是先 docker system df 看占用分布,只删悬空镜像:docker image prune -f;若确认没有未挂载但存有数据的卷,再考虑追加 --volumes

元凶三:MySQL binlog 堆积。 开启主从复制或默认 binlog 配置下,/var/lib/mysql 里的 binlog.0000xx 文件会持续增长,几天不管就是几十 GB。安全的清理方式不是手动 rm——那会破坏复制位点——而是登录 MySQL 执行:

PURGE BINARY LOGS BEFORE NOW() - INTERVAL 7 DAY;

这会删除 7 天前的所有 binlog,保留最近一周用于恢复与复制。再顺手设上限防止复发,在 my.cnf[mysqld] 段中加入:

binlog_expire_logs_seconds = 604800
max_binlog_size = 500M

第一条让 binlog 7 天自动过期(604800 秒),第二条限制单个文件 500MB。数据库类故障的更多处理思路,可延伸阅读 WordPress 数据库与主机实践

日志、镜像与 binlog 三类安全清理动作对照

四、删除前必须过一遍的安全清单

上面三类之外的文件,删除前建议走一遍简短的安全核对,避免”删时一时爽、恢复火葬场”。

先确认不是业务数据。 数据库文件(.ibd.frm)、网站目录、用户上传内容,这些永远不在”可删”清单里。判断方法:看路径是否在业务目录下、看文件修改时间是否活跃、用 lsof <文件> 确认没有进程正在使用。拿不准的文件,先移动到临时目录放三天再删,比直接 rm 稳妥得多。

优先截断而非删除。 所有正在被写入的日志,都用 > 清空代替 rm,这是上一节强调过的原则。清空后运行 df -h 确认空间真的回来了;若没有,lsof +L1 | sort -k7 -nr | head 找出占着句柄的进程。

删前留档。 重大清理前把待删清单落到一个文本文件里(find ... > /tmp/cleanup-list.txt),既方便复查,也便于事后向团队说明清掉了什么。生产环境的一次谨慎操作,胜过十次快速回滚。

如果反复排查后磁盘依然吃紧,问题可能不在垃圾文件而在容量本身——业务数据增长、备份策略保留过久都会推高基线。这时与其反复清理,不如评估 独立服务器 或更大磁盘的方案,Hostease 的服务器方案支持按需扩容,避免业务增长频繁撞上磁盘天花板。

五、预防:让磁盘永远不再”突然”爆满

应急处理做得再快,也不如让故障不发生。预防磁盘爆满只需要三道防线,成本都不高。

第一道是磁盘用量告警。与其等磁盘满了再救火,不如在 80% 时就收到通知。没有专业监控栈的情况下,cron 加一行检查就够用:每天执行 df -h 并对超过阈值的分区输出告警,配合邮件或即时通知即可。也可使用现成监控方案,例如基于 TTFB 主机优化指南 里提到的性能监控思路做扩展。

第二道是日志轮转兜底。确认 /etc/logrotate.conf/etc/logrotate.d/ 下各服务的配置生效,核心参数是 rotate 保留份数与 size 单文件上限。对 journal 设定持久上限:在 /etc/systemd/journald.conf 中写 SystemMaxUse=500M,重启 journald 后生效,journal 永远不会超过这个数。

第三道是定期空间审计。每周低峰期跑一次文章第二节的 du 下钻命令,把结果存档对比。空间增长的趋势比绝对值更有价值:这周 /var/lib/docker 涨了 5GB,下周又涨 5GB,说明镜像构建策略需要调整,而不是等它吃满磁盘。把这三个习惯固化下来,磁盘爆满会从”半夜事故”退化成”周报里的一行容量趋势”。

总结与行动建议

磁盘爆满的处理本质是一条清晰链路:df 确认分区 → du 逐层下钻 → find 锁定大文件 → 按类型安全清理 → 事后加预防。核心原则有三条:紧急时先清 journal 止血;删日志用截断不用 rm;binlog 用 PURGE 语句不用文件删除。建议你现在就做三件事:一是登服务器跑一遍 df -hTdf -i,了解自己离阈值还有多远;二是给 /etc/logrotate.d/ 和 journald 配置做一次核对,补上 SystemMaxUse 上限;三是把 du 下钻命令存成脚本,纳入每周巡检。如果你需要更省心的运维托管体验,也可以考虑 Hostease 的主机服务,把日常监控与容量管理交给专业团队分担。

发表评论