数据库每天定时备份和三个月才想起来备份一次,恢复的时候前者5分钟恢复全量数据后者3个月的用户和内容全丢,差距不在恢复工具上在备份频率和异地存储这两个前置环节
一台跑了15个WordPress站点的服务器,硬盘突然坏了。机房换上新硬盘,系统重装好,该恢复数据库了。打开备份目录一看——最近一次备份是三个月前。三个月里新增的800多篇文章、6000多条用户评论、两个月的订单记录,全部没了。不是没有备份工具,是备份脚本三个月前配好之后就没再管过,crontab里的定时任务不知道什么时候停了。数据库恢复这件事,上限不由恢复工具决定,由备份策略决定。
多站点场景下数据库管理有三个核心矛盾:站点数量多了之后备份脚本容易漏站、恢复时容易搞混库、备份文件只存在本地服务器硬盘一坏全丢。这篇文章把MySQL/WordPress数据库的批量备份和恢复讲清楚——用什么命令、怎么定时自动跑、怎么异地存储、恢复前怎么验证备份文件是可用的。
数据库恢复的四层保障,缺任何一层恢复时都可能翻车
| 1 | 定时自动备份:crontab每天凌晨跑,mysqldump全量备份 + binlog增量备份,不能靠手动 |
| 2 | 异地存储:备份文件自动同步到另一台服务器/对象存储/云盘,不和源数据库放在同一台机器上 |
| 3 | 恢复脚本就绪:恢复命令提前写好测试过,不要等到出事的时候才临时拼命令 |
| 4 | 定期恢复演练:每月抽一个备份文件恢复到测试环境,验证备份文件本身没有损坏 |
一、MySQL批量备份:mysqldump的正确用法
mysqldump是MySQL自带的数据导出工具,免费、稳定、兼容性好,90%的MySQL数据库备份恢复场景它都能搞定。它的核心逻辑是把数据库的表结构和数据导出为SQL文本文件,恢复时重新执行这些SQL语句即可重建数据库。

| 场景 | 命令 | 说明 |
|---|---|---|
| 备份单个数据库 | mysqldump -u root -p db_name > backup.sql | 最基本的用法,适合临时手动备份 |
| 备份所有数据库 | mysqldump -u root -p --all-databases > all_backup.sql | 整台服务器所有库一次性导出,恢复时也是全部恢复 |
| 备份多个指定库 | mysqldump -u root -p --databases db1 db2 db3 > multi.sql | 只备份指定的几个库,不碰其他库 |
| 备份所有库(分别文件) | 见下方批量备份脚本 | 每个库单独一个文件,恢复时按需选择——多站点场景最推荐的方式 |
多站点场景下最推荐的方式是每个数据库单独导出为一个文件,而不是一个--all-databases全量导出。原因很简单:15个站对应15个数据库,如果全量导出一个文件,恢复时必须全部恢复,没法只恢复某一个站。而分别导出的话,哪个站出问题恢复哪个,不会影响其他正常运行的站。
#!/bin/bash# 批量备份MySQL所有数据库(每个库单独一个文件)# 保存为 /opt/scripts/mysql_batch_backup.shBACKUP_DIR="/data/backups/mysql"DATE=$(date +%Y%m%d_%H%M%S)MYSQL_USER="root"MYSQL_PASS="你的密码"RETENTION_DAYS=30 # 保留最近30天的备份# 创建当天备份目录mkdir -p "$BACKUP_DIR/$DATE"# 获取所有数据库列表(排除系统库)DATABASES=$(mysql -u$MYSQL_USER -p$MYSQL_PASS -e "SHOW DATABASES;" | \grep -v "Database\|information_schema\|performance_schema\|mysql\|sys")# 逐个备份for db in $DATABASES; doecho "正在备份: $db"mysqldump -u$MYSQL_USER -p$MYSQL_PASS \--single-transaction \--routines \--triggers \--events \--set-gtid-purged=OFF \"$db" | gzip > "$BACKUP_DIR/$DATE/${db}.sql.gz"if [ $? -eq 0 ]; thenecho " ✓ $db 备份成功 ($(du -h "$BACKUP_DIR/$DATE/${db}.sql.gz" | cut -f1))"elseecho " ✗ $db 备份失败!"fidone# 清理超过保留天数的旧备份find "$BACKUP_DIR" -maxdepth 1 -type d -mtime +$RETENTION_DAYS -exec rm -rf {} \;echo "备份完成,备份目录: $BACKUP_DIR/$DATE"这个脚本的几个关键参数:--single-transaction保证InnoDB表在备份期间的一致性(不会锁表,不影响网站正常运行);--routines --triggers --events把存储过程、触发器、事件也一起备份(很多人只备份了表结构和数据,丢了这些东西);gzip压缩大幅减小文件体积,15个WP数据库压缩前可能3-5GB,压缩后通常只有500MB-1GB。
二、批量恢复:单个库恢复和全量恢复的完整命令
备份做完了,恢复才是关键时刻。恢复分两种场景:单个库恢复(某一个站的数据出了问题需要回滚)和全量恢复(服务器挂了所有数据库都需要重建)。
#!/bin/bash# 批量恢复MySQL数据库# 用法: ./mysql_batch_restore.sh [单个数据库名或"all"]BACKUP_DATE="$2" # 指定恢复哪个日期的备份,如 20250728_030000BACKUP_DIR="/data/backups/mysql/$BACKUP_DATE"MYSQL_USER="root"MYSQL_PASS="你的密码"restore_single_db() {local db=$1local backup_file="$BACKUP_DIR/${db}.sql.gz"if [ ! -f "$backup_file" ]; thenecho "✗ 备份文件不存在: $backup_file"return 1fiecho "正在恢复: $db"# 先删除旧库再重建(确保干净恢复)mysql -u$MYSQL_USER -p$MYSQL_PASS -e "DROP DATABASE IF EXISTS \`$db\`; CREATE DATABASE \`$db\`;"gunzip < "$backup_file" | mysql -u$MYSQL_USER -p$MYSQL_PASS "$db"if [ $? -eq 0 ]; thenecho " ✓ $db 恢复成功"elseecho " ✗ $db 恢复失败!"fi}restore_all() {echo "===== 开始批量恢复所有数据库 ====="local total=0local success=0for f in "$BACKUP_DIR"/*.sql.gz; doif [ -f "$f" ]; thendb=$(basename "$f" .sql.gz)total=$((total+1))restore_single_db "$db" && success=$((success+1))fidoneecho "===== 恢复完成: $success/$total 个数据库成功 ====="}# 主逻辑if [ "" = "all" ]; thenrestore_allelif [ -n "" ]; thenrestore_single_db ""elseecho "用法: [数据库名|all] [备份日期]"echo "示例: wp_site1 20250728_030000"echo "示例: all 20250728_030000"fi
恢复时有一个容易被忽略的操作:。如果直接在现有数据库上导入SQL文件,可能会遇到表已存在、数据冲突等问题。先删干净再重建,确保恢复后的数据库和备份时完全一致。
恢复前必须做的三件事:
· 确认备份文件大小是否正常(和之前备份的大小对比,异常偏小可能损坏)
gunzip -t backup.sql.gz
检查压缩文件完整性· 在生产环境恢复前,先在测试环境跑一遍确认SQL文件可用
三、WordPress数据库专项:插件 vs 命令行,场景不同选择不同UpdraftPlus(免费+付费)· WP插件市场占有率最高的备份插件
· 支持定时备份到Google Drive/Dropbox/S3等远程存储
· 一键恢复,自动处理URL替换
WP Migrate DB(付费)· 专门做数据库迁移,不是备份工具
· 导出时自动序列化数据处理(URL替换不出错)
· 支持推/拉模式,直接从A站迁移到B站
mysqldump + WP-CLI· 命令行方案,适合批量操作
· mysqldump导出 + WP-CLI search-replace替换URL
· 配合crontab实现全自动备份+异地存储
All-in-One WP Migration· 免费版限制512MB,超过要付费
· 导出整个站点(文件+数据库)为单个文件
· 导入时自动处理URL替换
多站点场景下的WP数据库批量备份,最推荐的组合是
mysqldump做全量备份 + WP-CLI做迁移时的URL替换。mysqldump负责每天定时导出所有WP数据库,WP-CLI负责在需要迁移到新服务器时批量替换域名:
# WP-CLI批量替换域名(迁移到新服务器后执行)wp search-replace 'http://旧域名.com' 'https://新域名.com' \--all-tables --precise --dry-run # 先dry-run预览,确认无误后去掉--dry-run正式执行# 遍历所有WP站点批量替换for site in /var/www/*/; docd "$site"if [ -f wp-config.php ]; thenecho "处理: $site"wp search-replace 'oldsite.com' 'newsite.com' --all-tables --precisefidone
四、定时备份+异地存储:不把鸡蛋放在同一个篮子里云存储(推荐)S3/OSS/COS
另一台服务器rsync/scp
网盘(备选)rclone
在备份脚本后面加几行,自动把备份文件同步到云存储。以阿里云OSS为例,用ossutil命令行工具:
# 在mysql_batch_backup.sh末尾追加异地同步# 同步到阿里云OSS(提前安装ossutil并配置好AK)ossutil cp -r "$BACKUP_DIR/$DATE" oss://你的bucket名/mysql-backups/ --update# 或者同步到另一台服务器rsync -avz --delete "$BACKUP_DIR/" backup@192.168.1.100:/data/backups/mysql/# 或者用rclone同步到Google Driverclone copy "$BACKUP_DIR/$DATE" gdrive:mysql-backups/
设置crontab让备份脚本每天凌晨3点自动跑:
# crontab -e 添加以下行# 每天凌晨3点执行数据库备份0 3 * * * /bin/bash /opt/scripts/mysql_batch_backup.sh >> /var/log/mysql_backup.log 2>&1# 每周日凌晨4点检查备份日志并发送汇总邮件(可选)0 4 * * 0 tail -20 /var/log/mysql_backup.log | mail -s "数据库备份周报" admin@example.com
五、备份策略设计:全量、增量、保留周期| 策略 | 方式 | 恢复粒度 | 存储成本 | |
|---|---|---|---|---|
| 每日全量 | 每天凌晨mysqldump全部库 | 可恢复到任意一天 | 高(15个站×30天=450个备份文件) | |
| 全量+增量 | 每周日全量 + 每天binlog增量 | 可恢复到任意时间点 | 中(7个全量+30天增量日志) | |
| 保留策略 | 本地7天 + 云存储30天 + 归档90天 | 近期高频恢复,远期低频 | 阶梯式降低 |
对于多站点WP场景,
是最实用的组合。mysqldump全量备份操作简单,不需要配置binlog。本地保留7天用于快速恢复(最近的故障往往在7天内发现),云存储保留30天用于更早的数据恢复。超过30天的备份归档到冷存储,成本极低。
备份保留策略的一个关键原则:
不是存得越久越好,而是覆盖的时间窗口要足够你发现数据问题。有些数据损坏不是立刻暴露的——比如一篇文章被误删了,可能两周后才有人发现。如果备份只保留7天,两周前的备份已经被清理了,数据就真丢了。30天是一个比较安全的窗口。
备份脚本每天在跑,日志显示"备份成功",云存储上文件大小也正常。等到真需要恢复的时候才发现——备份文件是损坏的,SQL语法有错误,或者导出时漏了某个关键表。这不是假设,是真实发生过的。
恢复验证的核心操作很简单:每月抽一个备份文件,恢复到一台测试服务器上,确认数据完整可用。不需要全部恢复,只需要随机抽一个库验证。如果15个站点,每月随机抽3个库恢复验证,三个月轮完一轮。< "$RANDOM_FILE" | mysql -u$MYSQL_USER -p$MYSQL_PASS "$TEST_DB"if [ $? -eq 0 ]; then# 验证:查表数量和行数TABLE_COUNT=$(mysql -u$MYSQL_USER -p$MYSQL_PASS -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='$TEST_DB';" -N)echo "✓ 恢复成功!共 $TABLE_COUNT 张表"# 清理测试库mysql -u$MYSQL_USER -p$MYSQL_PASS -e "DROP DATABASE \`$TEST_DB\`;"elseecho "✗ 恢复失败!备份文件可能损坏: $RANDOM_FILE"# 发送告警fi
#!/bin/bash# 恢复验证脚本:随机抽取一个数据库恢复到测试环境# 在测试服务器上运行BACKUP_DIR="/mnt/backups/mysql"TEST_DB_PREFIX="verify_" # 测试恢复的数据库名前缀MYSQL_USER="root"MYSQL_PASS="测试环境密码"# 随机选一个备份日期RANDOM_DATE=$(ls "$BACKUP_DIR" | sort -R | head -1)echo "验证日期: $RANDOM_DATE"# 随机选一个数据库备份文件RANDOM_FILE=$(ls "$BACKUP_DIR/$RANDOM_DATE"/*.sql.gz | sort -R | head -1)DB_NAME=$(basename "$RANDOM_FILE" .sql.gz)TEST_DB="${TEST_DB_PREFIX}${DB_NAME}"echo "验证数据库: $DB_NAME -> $TEST_DB"# 恢复到测试库mysql -u$MYSQL_USER -p$MYSQL_PASS -e "DROP DATABASE IF EXISTS \`$TEST_DB\`; CREATE DATABASE \`$TEST_DB\`;"gunzip
这个脚本每月跑一次,放到crontab里。如果恢复失败立即告警,说明备份脚本或备份文件出了问题,赶在真正需要恢复之前修复。
多站点场景下,手工管理这些备份脚本、crontab、异地同步、恢复验证,20个站每台服务器都要配一遍,容易漏也容易忘。用UC建站系统
的多站看板可以把所有站点的数据库备份状态统一监控起来——哪个站的备份成功了、哪个失败了、备份文件大小异常、异地同步是否完成,一个看板全看到,不用登录每台服务器检查。七、数据库损坏修复:当备份也不够新的时候
MySQL内置修复
REPAIR TABLE 表名;
innodb_force_recovery=1~6(强制恢复模式)
mysqlcheck -r 数据库名
批量修复所有表phpMyAdmin修复· 选中数据库 → 勾选所有表 → 下拉菜单选"修复表"
· 图形化操作,不需要命令行
· 适合不熟悉命令行的用户
Percona Data Recovery Tool· 从损坏的InnoDB .ibd文件中提取数据
· 适用于MySQL无法启动但数据文件还在的情况
· 需要一定的MySQL底层知识
数据库修复有一个铁律:修复之前先备份当前的数据库文件(包括损坏的)。
innodb_force_recovery
参数从1到6逐级递增,级别越高修复越激进也越危险,从1开始试,不行再升一级。

