昨天晚上收到反馈说服务用不了,上去一看:数据库报「Too many connections」,再往下翻,MySQL 日志里全是「Disk is full」。一台 40G 的小服务器,硬盘被数据库备份撑满了。这篇记录排查过程,以及为什么不建议在服务器本地用 git 存数据库备份。
现象
应用日志:
Error 1040: Too many connectionsMySQL 日志:
Disk is full writing './binlog.000002' (OS errno 28 - No space left on device).
Waiting for someone to free space...注意第二条:磁盘满的时候 MySQL 不会直接报错退出,而是卡住等空间,每 60 秒重试一次。写入卡住、连接释放不掉,越堆越多,最后就是 Too many connections。所以看到连接数爆了,别只想着调 max_connections,先 df -h 看一眼磁盘。
找出谁占了空间
df -h /
du -xsh /opt/* /var/lib/docker /var/log 2>/dev/null | sort -h | tail结果:
27G /opt/db-backup40G 的盘,备份目录占了 27G。
为什么会这么大
备份脚本的逻辑是:每小时 mysqldump | gzip 一份,放进一个 git 仓库,git commit 后推到 GitHub 私有仓库,本地只保留最近 500 份。
看起来很合理,但有两个问题:
- git 会保留所有历史版本。工作区里删掉旧文件,
.git里的对象还在。每小时一份压缩包,压缩包之间又没法增量压缩,.git只会越来越大。查下来.git自己就有 16G。 - 同一个仓库里还有别的机器推上来的备份,每次
git pull都会把它们拉到本地,又多占了好几个 G。
应急:先腾出一点空间让数据库活过来
最快的是清 docker 容器日志(docker 默认不限制日志大小):
find /var/lib/docker/containers -name "*-json.log" -size +100M -exec truncate -s 0 {} \;清掉不到 1G,MySQL 自己就恢复写入了,不用重启。
根治:服务器本地不留仓库
备份照样推到 GitHub,但本地不再保留仓库。每次备份时:
在临时目录做一个浅克隆,并且不下载任何旧的备份文件:
git clone --depth 1 --filter=blob:none --no-checkout "$URL" "$WORK/repo" git sparse-checkout set --no-cone /README.md- 把这次的 dump 放进去,用
git add --sparse加入,再从索引里删掉超过 500 份的旧文件 - 提交、推送,然后删掉整个临时目录
改完以后,服务器硬盘从 100% 降到 27%,每小时的备份照常推上去,本地一个字节都不多占。
另外建议给 docker 加上日志轮转,/etc/docker/daemon.json:
{ "log-driver": "json-file", "log-opts": { "max-size": "50m", "max-file": "3" } }小结
- 连接数突然爆满,先看磁盘
- 磁盘满时 MySQL 是卡住而不是报错退出
- git 不适合在本地长期存大量二进制备份,历史只增不减
- 要用 git 存备份,就用浅克隆 + 稀疏检出,推完即删
- docker 日志记得限大小
希望这篇能帮你提前避开这个坑 ( ˘ω˘ )
(这是我赶着写的,用了AI帮忙润色了一下嘻嘻)