昨天晚上收到反馈说服务用不了,上去一看:数据库报「Too many connections」,再往下翻,MySQL 日志里全是「Disk is full」。一台 40G 的小服务器,硬盘被数据库备份撑满了。这篇记录排查过程,以及为什么不建议在服务器本地用 git 存数据库备份。

现象

应用日志:

Error 1040: Too many connections

MySQL 日志:

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-backup

40G 的盘,备份目录占了 27G。

为什么会这么大

备份脚本的逻辑是:每小时 mysqldump | gzip 一份,放进一个 git 仓库,git commit 后推到 GitHub 私有仓库,本地只保留最近 500 份。

看起来很合理,但有两个问题:

  1. git 会保留所有历史版本。工作区里删掉旧文件,.git 里的对象还在。每小时一份压缩包,压缩包之间又没法增量压缩,.git 只会越来越大。查下来 .git 自己就有 16G。
  2. 同一个仓库里还有别的机器推上来的备份,每次 git pull 都会把它们拉到本地,又多占了好几个 G。

应急:先腾出一点空间让数据库活过来

最快的是清 docker 容器日志(docker 默认不限制日志大小):

find /var/lib/docker/containers -name "*-json.log" -size +100M -exec truncate -s 0 {} \;

清掉不到 1G,MySQL 自己就恢复写入了,不用重启。

根治:服务器本地不留仓库

备份照样推到 GitHub,但本地不再保留仓库。每次备份时:

  1. 在临时目录做一个浅克隆,并且不下载任何旧的备份文件:

    git clone --depth 1 --filter=blob:none --no-checkout "$URL" "$WORK/repo"
    git sparse-checkout set --no-cone /README.md
  2. 把这次的 dump 放进去,用 git add --sparse 加入,再从索引里删掉超过 500 份的旧文件
  3. 提交、推送,然后删掉整个临时目录

改完以后,服务器硬盘从 100% 降到 27%,每小时的备份照常推上去,本地一个字节都不多占。

另外建议给 docker 加上日志轮转,/etc/docker/daemon.json:

{ "log-driver": "json-file", "log-opts": { "max-size": "50m", "max-file": "3" } }

小结

  • 连接数突然爆满,先看磁盘
  • 磁盘满时 MySQL 是卡住而不是报错退出
  • git 不适合在本地长期存大量二进制备份,历史只增不减
  • 要用 git 存备份,就用浅克隆 + 稀疏检出,推完即删
  • docker 日志记得限大小

希望这篇能帮你提前避开这个坑 ( ˘ω˘ )
(这是我赶着写的,用了AI帮忙润色了一下嘻嘻)

Last modification:October 16, 2026