mysqldump + cron 是唯一靠谱的自动化备份方案,因ThinkPHP的db:backup不支持事务一致性、不导出存储过程与触发器、大表易崩、无完整性校验且静默失败。

直接用 mysqldump + cron 是唯一靠谱的自动化方案,ThinkPHP 自带的 php think db:backup 不适合生产环境自动备份——它不支持事务一致性、不导出存储过程、大表必崩,且无法校验备份完整性。
为什么不能用 ThinkPHP 的命令行备份做自动化
很多人图省事直接加个 cron 跑 php think db:backup,结果半夜备份失败却没报警,第二天才发现文件是空的或缺了三张表。
- 遇到日志表超 50 万行,PHP 内存耗尽,进程被 kill,生成空文件但返回码仍是 0
- 不加
--single-transaction,备份中途数据被改,导出的是“半截快照” - 完全不导出
TRIGGERS和ROUTINES,恢复后定时任务、权限钩子全失效 - 没有字符集显式声明,utf8mb4 表里的 emoji 变成
???,且无任何警告 - 某张表权限不足(比如只读用户),它会静默跳过,不报错也不记录
怎样写一个真正可用的自动备份脚本
核心是绕开 PHP 框架层,让系统级工具干它该干的事。以下是一个最小可行脚本(backup.sh),放在项目根目录下即可被 cron 调用:
#!/bin/bash
DB_NAME="your_db_name"
BACKUP_DIR="/data/backups"
CNF_FILE="/data/www/my.cnf"
<h1>确保路径存在且 Web 进程可写(如 www-data)</h1><p>mkdir -p "$BACKUP_DIR"</p><p><span>立即学习</span>“<a href="https://pan.quark.cn/s/7fc7563c4182" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">PHP免费学习笔记(深入)</a>”;</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/skill5233" title="btpanel phpsite 宝塔面板PHP网站"><img
src="https://img.php.cn/upload/skill/000/000/081/179040786932301.jpg" alt="btpanel phpsite 宝塔面板PHP网站" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/skill5233" title="btpanel phpsite 宝塔面板PHP网站">btpanel phpsite 宝塔面板PHP网站</a>
<p>宝塔面板 PHP 网站管理:站点创建、删除、启停、PHP 版本切换、域名管理、SSL证书管理、伪静态管理、数据库管理</p>
</div>
<a href="/xiazai/skill5233" title="btpanel phpsite 宝塔面板PHP网站" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div><h1>用配置文件传参,避免密码泄露</h1><p>/usr/bin/mysqldump \
--defaults-file="$CNF_FILE" \
--default-character-set=utf8mb4 \
--single-transaction \
--routines \
--triggers \
--events \
--hex-blob \
--set-gtid-purged=OFF \
"$DB_NAME" > "$BACKUP_DIR/${DB<em>NAME}</em>$(date +\%Y\%m\%d_\%H\%M\%S).sql"</p><h1>校验是否真的生成了内容(防空文件)</h1><p>if [ ! -s "$BACKUP_DIR/${DB<em>NAME}</em>$(date +\%Y\%m\%d_\%H\%M\%S).sql" ]; then
echo "ERROR: backup file is empty" | logger -t db-backup
exit 1
fi</p><h1>保留最近 7 天</h1><p>find "$BACKUP_DIR" -name "${DB<em>NAME}</em>*.sql" -mtime +7 -delete关键点:
-
CNF_FILE必须是真实存在的 MySQL 配置文件,权限设为600(chmod 600 /data/www/my.cnf) - 所有路径用绝对路径,
/usr/bin/mysqldump不能简写为mysqldump,避免PATH不一致 - 必须检查文件大小:
-s判断非空,否则 cron 成功但备份无效 - 日志走
logger,方便统一收集,别只 echo 到终端
cron 配置与权限陷阱
在服务器上执行 crontab -u www-data -e(不是 root),加一行:
0 2 * * * /bin/bash /data/www/backup.sh >> /data/www/logs/backup.log 2>&1
常见失败原因:
-
www-data用户没加入mysql组(Debian/Ubuntu),导致连不上本地 socket;执行sudo usermod -a -G mysql www-data -
mysqldump找不到:确认which mysqldump输出路径,并在脚本里写死 - MySQL 用户缺少
LOCK TABLES权限(--single-transaction仍需该权限用于非 InnoDB 表兜底),执行:GRANT LOCK TABLES ON your_db_name.* TO 'your_user'@'localhost'; - 备份目录磁盘满或 inodes 耗尽,
df -h和df -i都得看
备份后必须做的三件事
自动化不是设完 cron 就完事。真出问题时,能快速判断“这次备份到底能不能用”,靠的是这三步:
- 每次备份后立刻跑
sha256sum /data/backups/xxx.sql > /data/backups/xxx.sql.sha256,防止静默损坏 - 每周抽一份备份,用
mysql -u user -p --default-character-set=utf8mb4 target_db < xxx.sql实际还原测试(别只看文件存在) - 把备份文件路径、SHA256 值、时间戳写入数据库日志表(如
backup_log),而不是只靠文件名猜
最常被忽略的一点:备份脚本里没写 set -e,某个 mysqldump 失败后,后续的 find 删除旧文件照常执行,等于把最后有效的备份也删了。


















