网站运维

磁盘满了网站就挂:MySQL binlog 和日志的清理节奏

网站突然打不开,数据库连接失败,后台登录不进去,重启服务器也没用。

这种时候先别慌着查代码,跑一条命令:

df -h

如果某个分区 Use% 显示 100%,原因就找到了。而且我可以先告诉你最可能的元凶:MySQL 的 binlog 日志。

一、为什么磁盘满了网站会完全挂掉

MySQL 写不进数据就会拒绝连接,WordPress 拿不到数据直接报错;PHP 写不了 session 文件,用户全部登录失败;nginx 写不了访问日志,请求直接 500。

所以磁盘满不是”慢一点”,是整站瘫痪。而且很多人遇到这种情况第一反应是重启——重启解决不了磁盘满,白白浪费时间。

二、3 分钟定位到底是谁占满了

第一步:确认哪个分区满了

df -h

一般是 / 根分区。

第二步:找出占空间的大目录

du -sh /* 2>/dev/null | sort -rh | head -15

然后一层层往下钻,比如发现 /www 最大,就:

du -sh /www/* 2>/dev/null | sort -rh | head -15

第三步:几种常见”大户”

占用来源 典型路径 能不能删
MySQL binlog /www/server/data/ 或 /var/lib/mysql/ 下的 mysql-bin.0000xx 能,但要按规矩清
网站访问日志 /www/wwwlogs/ 能删,但先保留最近的
数据库慢日志/错误日志 /www/server/data/*.err 能删
站点备份文件 /www/backup/ 看情况,异地备份过的可删
未清理的临时文件 /tmp/ 基本都能删
已删除但被进程占用的文件 无路径(句柄) 重启对应进程才释放

一个容易踩的坑:文件删了但空间没释放

如果你用 rm 删了大文件,df -h 显示空间没变,是因为那个文件还被某个进程占着。查:

lsof | grep deleted

找到对应的进程(通常是 mysqld 或 nginx),重启它,空间才会真正释放。

三、binlog:撑爆磁盘的头号选手

binlog 是 MySQL 的二进制日志,记录所有数据变更。它的作用是数据恢复和主从同步。如果你没做主从复制,它对你唯一的价值就是”万一要恢复到某个时间点”。

问题是:它默认不会自动清理。一个日访问几千的站,binlog 能以每天几百 MB 的速度增长。我见过 40G 的硬盘被 binlog 吃到 39G。

看看你的 binlog 占了多大

du -sh /www/server/data/mysql-bin.* 2>/dev/null | tail -1
# 或进 MySQL 看
mysql -uroot -p -e "SHOW BINARY LOGS;"

安全清理(推荐做法)

登录 MySQL,按时间清理:

-- 清理 3 天前的所有 binlog
PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 3 DAY);

-- 或清理到某个具体文件之前
PURGE BINARY LOGS TO 'mysql-bin.000123';

不要用 rm 直接删 binlog 文件,会导致 MySQL 索引错乱,下次启动可能起不来。

治本:设置自动过期

MySQL 5.7 / 8.0 在配置文件加:

[mysqld]
expire_logs_days = 3          # 8.0 用 binlog_expire_logs_seconds
max_binlog_size = 100M

MySQL 8.0 推荐:

binlog_expire_logs_seconds = 259200   # 3天

改完重启 MySQL 生效。

如果你根本用不到 binlog

单服务器、没有主从同步的独立站,其实可以直接关掉 binlog,省事又省空间。在 my.cnf 里注释掉 log-bin 那行,重启 MySQL。

代价是:就不能做”恢复到某一秒”的点对点恢复了。但如果你本来就是每天全量备份,这个代价可以接受——大不了丢一天数据,而实际上独立站丢一天数据的概率极低。

怎么选?

  • 电商站有订单:保留 binlog,设 3 天过期
  • 纯展示型企业站:直接关掉,靠每日全量备份

四、日志:另一个慢性杀手

网站访问日志看着不大,但高流量站一天几百 MB 很正常。而且宝塔默认不自动切割,一个 access.log 能长到十几 G。

开启日志切割

宝塔面板:站点设置 → 日志 → 开启”日志切割”,设置保留天数(建议 7-14 天)。

或者用系统自带的 logrotate,配置 /etc/logrotate.d/nginx:

/www/wwwlogs/*.log {
    daily
    rotate 14
    compress
    missingok
    notifempty
    create 0644 www www
    postrotate
        [ -f /www/server/nginx/logs/nginx.pid ] && kill -USR1 `cat /www/server/nginx/logs/nginx.pid`
    endscript
}

处理已经被写爆的日志文件

注意:不要直接 rm access.log。nginx 还持有这个文件句柄,你删了它还会继续往那个已删除的文件写,空间不释放。

正确做法是清空:

> /www/wwwlogs/你的站点.log

这个命令把文件内容截断为 0,不影响 nginx 的句柄,空间立刻释放。

五、让它不再发生:三道防线

1. 磁盘告警

宝塔面板 → 设置 → 告警,开启磁盘告警,阈值设 80%。或者用免费的监控服务(UptimeRobot 的磁盘监控、Server酱推送)。

关键是要提前知道,而不是等网站挂了才发现。

2. 定期清理自动化

写个脚本放 cron,每周跑一次:

#!/bin/bash
# 清理 7 天前的日志
find /www/wwwlogs -name "*.log-*" -mtime +7 -delete
# 清理临时文件
find /tmp -type f -mtime +7 -delete
# MySQL 清理 3 天前 binlog
mysql -uroot -p密码 -e "PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 3 DAY);"

3. 备份异地化

本地备份文件也是磁盘大户。备份一定要传到异地(对象存储、另一台机器、网盘),本机只保留最近 2-3 份。

这不只是省空间的问题——服务器整个挂掉的时候,本地备份跟着一起没。

最后

磁盘满这件事,处理本身不难,难的是它总是在你不注意的时候发生。

花 20 分钟把 binlog 过期、日志切割、磁盘告警三件事配好,能省掉未来某一天几个小时的救火。运维里绝大多数”事故”,都是本可以用十分钟配置避免的。