搜索全站
网站迁移 10 分钟阅读

备份恢复演练:如何验证 WordPress 备份真的可以恢复

80% 的备份从未被验证过,真正需要时才发现不可用。本文给出一套可执行的 WordPress 备份恢复演练流程:文件完整性校验、数据库导入测试、站点可用性验证、恢复时间记录,以及每月演练的检查清单与自动化脚本。

U
UFO_PF 2021-04-14 · 阅读 10 分钟
备份 网站迁移
阅读提示
本文约 3,709 字,预计阅读 10 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:未经验证的备份,等于没有备份

备份失败的几种典型形态:

  • 备份文件是 0 字节(脚本密码写错);
  • 备份是三个月前的(cron 没跑起来);
  • 备份缺了 wp-content(排除规则写错);
  • 备份能导入,但字符集不对,中文全乱码。

这些只有在恢复时才会暴露。所以恢复演练不是”可选”,而是备份流程的一部分。

原理分析:验证要覆盖四个层面

层面 验证什么 方法
完整性 文件是否完整、未损坏 大小校验、gzip -t、校验和
可导入性 SQL 能否成功执行 导入测试库
内容正确性 数据是否完整、字符集是否正常 抽查记录数、中文与 emoji
站点可用性 恢复后站点能否正常访问 启动站点、访问前台后台

只做第一步是不够的——文件完整但导入失败的情况很常见(字符集、排序规则、语法差异)。

保姆级实操

第一步:文件完整性校验

# 大小检查
ls -lh /backup/db/*.sql.gz | tail -5

# gzip 完整性
gzip -t /backup/db/db-2026-10-10.sql.gz && echo "OK"

# 校验和比对(备份时生成,恢复时验证)
sha256sum /backup/db/db-2026-10-10.sql.gz

生成校验和的备份脚本片段:

sha256sum "$FILE" >> "${BK}/checksums.txt"

第二步:导入到测试库

mysql -e "DROP DATABASE IF EXISTS wp_restore_test;"
mysql -e "CREATE DATABASE wp_restore_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"

gunzip < /backup/db/db-2026-10-10.sql.gz | mysql wp_restore_test
echo "导入退出码: $?"

第三步:内容抽查

mysql wp_restore_test -e "
SELECT COUNT(*) AS 文章数 FROM wp_posts WHERE post_status='publish';
SELECT COUNT(*) AS 用户数 FROM wp_users;
SELECT option_value FROM wp_options WHERE option_name='home';
SELECT post_title FROM wp_posts WHERE post_status='publish' LIMIT 3;
"

重点关注:

  • 文章数与生产库是否接近;
  • home 选项的值是否正确;
  • 中文标题是否正常显示(不是 ??? 或乱码);
  • 是否有 emoji 能正常读出。

第四步:恢复站点文件并启动

在一台测试机(或本机另一个目录)完整还原:

mkdir -p /tmp/restore-test
tar xzf /backup/files/files-2026-10-10.tar.gz -C /tmp/restore-test

创建测试库、修改 wp-config.php 指向测试库,配一个临时 Nginx server 块(用不同端口或测试域名),然后访问:

curl -I http://127.0.0.1:8080/

检查:

  • 首页返回 200;
  • CSS/JS 资源能加载(不是 404);
  • 后台能登录;
  • 媒体库图片能显示。

第五步:用 WP-CLI 验证

cd /tmp/restore-test
wp --path=/tmp/restore-test core verify-checksums
wp --path=/tmp/restore-test option get home
wp --path=/tmp/restore-test plugin list --status=active
wp --path=/tmp/restore-test db check

第六步:记录恢复时间(RTO)

用 time 记录整个过程耗时:

time (gunzip < db.sql.gz | mysql wp_restore_test)

这是你真实的 RTO 数据。如果恢复要 40 分钟而业务只能容忍 10 分钟,说明备份方案需要调整(比如改用快照或增量备份)。

第七步:把演练脚本化

#!/usr/bin/env bash
# /usr/local/bin/verify-backup.sh
set -uo pipefail
DB_BACKUP="${1:?用法: $0 <备份.sql.gz>}"
TEST_DB="wp_restore_test"
LOG="/var/log/backup-verify.log"

exec >> "$LOG" 2>&1
echo "=== $(date) 开始验证 $DB_BACKUP ==="

# 1. 完整性
gzip -t "$DB_BACKUP" || { echo "FAIL: 文件损坏"; exit 1; }

# 2. 导入
mysql -e "DROP DATABASE IF EXISTS $TEST_DB; CREATE DATABASE $TEST_DB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
gunzip < "$DB_BACKUP" | mysql $TEST_DB || { echo "FAIL: 导入失败"; exit 1; }

# 3. 抽查
POSTS=$(mysql -N -e "SELECT COUNT(*) FROM $TEST_DB.wp_posts WHERE post_status='publish';")
HOME=$(mysql -N -e "SELECT option_value FROM $TEST_DB.wp_options WHERE option_name='home';")
echo "文章数: $POSTS"
echo "站点地址: $HOME"

if [ "$POSTS" -lt 1 ]; then
  echo "FAIL: 文章数为 0"; exit 1
fi

# 4. 清理
mysql -e "DROP DATABASE $TEST_DB;"
echo "PASS: 备份可用"

每月定时执行(选最近一份备份):

0 5 1 * * /usr/local/bin/verify-backup.sh $(ls -t /backup/db/*.sql.gz | head -1)

第八步:演练检查清单(每月打勾)

  • [ ] 最近一次备份文件存在且非空
  • [ ] gzip 完整性通过
  • [ ] 能成功导入测试库
  • [ ] 文章数与生产库一致(±5%)
  • [ ] 中文与 emoji 正常
  • [ ] 站点能启动、后台能登录
  • [ ] 媒体库图片正常
  • [ ] 记录本次 RTO
  • [ ] 异地备份同样抽检一次

常见踩坑与失败案例

  1. 演练也在生产库上做 —— 千万别。DROP DATABASE 写错对象就是灾难。永远用独立的测试库,且脚本里写死库名。
  2. 案例:某公司每季度演练,但一直只做”文件完整性校验”,从未真的导入。真正出事时发现 SQL 里有 MariaDB 不认识的排序规则,导入失败。教训是必须真的导入一次。
  3. 测试库与生产库字符集不同 —— 导入成功但中文乱码,误判为备份损坏。创建测试库时要显式指定 utf8mb4。
  4. 演练完忘记清理测试库 —— 占用磁盘。脚本最后加 DROP DATABASE。
  5. 只在本地验证,异地备份从未验证 —— 异地备份同样要抽检,至少每季度下载一份验证。

总结

恢复演练的核心动作就四个:校验完整性 → 导入测试库 → 抽查内容 → 启动验证。前两个能发现 90% 的问题,后两个能发现剩下 10%(字符集、路径、权限)。把它做成脚本每月自动跑一次,结果写进运维日志,你会发现很多”以为没问题”的备份其实早就有问题。备份这件事,验证过的才算数。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。