WordPress 数据库备份与恢复:策略、工具与验证
数据库备份只有一条评价标准:出事的当天,你能不能在半小时内把站点恢复到另一台机器上,并且前台能正常打开。按这条标准衡量,很多站点的「备份」其实只是把一份 sql 文件放在网站根目录,既没验证过能不能导入,也没离开过这台服务器。
备份到底要备什么
WordPress 的数据分两半:一半在数据库里,一半在文件系统里。只备数据库,恢复之后文章都在、图片全是红叉,因为附件记录指向的文件没了;只备文件,恢复之后所有页面都是 404,因为固定链接和文章内容都在库里。两边必须配套。
数据库这一份,重点是表结构和数据一起备。只导数据不导结构,恢复时如果目标库是空的,表都建不出来;只导结构不导数据,那是给开发搭环境用的,不是备份。另外 wp-config.php 一定要单独留一份,它不在数据库里,但恢复时没有它连不上库,里面的盐值和数据库账号密码换了之后所有登录态都会失效。
| 备份对象 | 典型体积 | 变更频率 | 建议频率 |
|---|---|---|---|
| 数据库(结构 + 数据) | 20MB 至 500MB | 每天都有新内容 | 每日一次,保留 7 天 |
wp-content/uploads |
200MB 至 10GB | 只在发新图时变 | 每周全量,其余增量 |
wp-config.php |
几 KB | 几乎不变 | 改动时立刻另存 |
| 主题与插件目录 | 50MB 至 300MB | 升级时变 | 升级前打一次点 |
| 伪静态与 Nginx 配置 | 几 KB | 很少变 | 改动时立刻另存 |
主题和插件目录看似可以从官方仓库重新下载,但定制过的主题、改过的插件、已经下架的老版本,重新下载回来的和原来不是一回事。升级前把 wp-content/plugins 和 wp-content/themes 打个压缩包,出问题能整体回滚,比逐个找版本省事得多。
mysqldump 的正确写法
服务器上最可靠的方式还是命令行。mysqldump 是 MySQL 自带的,不依赖 PHP 的执行时间和内存,几 GB 的库也能导。
mysqldump -u wpuser -p
--single-transaction
--quick
--default-character-set=utf8mb4
--routines --triggers
--add-drop-table
wp_db | gzip > /home/backup/wp_db_$(date +%F_%H%M).sql.gz
几个参数值得逐个说清。--single-transaction 对 InnoDB 表开启一致性快照,导出过程中不锁表,前台照常读写;如果库里还有 MyISAM 表,这个参数管不住,要改用 --lock-tables,代价是导出期间写入被阻塞。--quick 让 mysqldump 逐行取数据而不是把整张表读进内存,大表必备,缺了它容易在导出中途被 OOM 杀掉。
--routines --triggers 别漏。电商、会员、统计类插件经常带存储过程和触发器,默认不导出,恢复后功能会莫名其妙失效。--default-character-set=utf8mb4 也是必须的,少了它中文可能变成问号,emoji 直接丢失。
三种变体记一下:只导结构加 --no-data,适合搭测试环境;只导数据加 --no-create-info,适合往已有结构的库里灌数据;要跳过某张大表(比如日志表)用 --ignore-table=wp_db.wp_xxx_log。
恢复:一条命令和三个前置条件
gunzip < /home/backup/wp_db_2026-11-03_0300.sql.gz | mysql -u wpuser -p wp_db
导入之前有三件事要先确认。第一,目标库必须存在且是空的,dump 里虽然带了 DROP TABLE,但不会帮你建库。第二,库的字符集要设成 utf8mb4,建库时用 CREATE DATABASE wp_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;,字符集不匹配会导致中文乱码,而且导入过程不报错。第三,把 max_allowed_packet 临时调到 64M 以上,否则大文件导入到一半会报 MySQL server has gone away。
如果是把备份恢复到新服务器,导入完还要改 wp-config.php 里的库名和账号,再处理域名替换。域名替换不要在 SQL 里直接 REPLACE,WordPress 的表里有大量序列化数据,字符串长度写死在记录里,直接替换会让序列化失效,表现为小工具全空、主题设置丢失。用 wp search-replace 'https://old.com' 'https://new.com' --all-tables --precise,它会把序列化结构一起修好。迁移搬家 里还有更完整的换域名流程。
插件方案与手动方案的分界
不是所有人都能登服务器敲命令,插件方案有它的位置。但两者解决的问题不一样:插件解决「定时、自动、传到云端」,手动解决「可控、可验证、不受插件自身故障影响」。
| 方案 | 适用场景 | 代价与风险 |
|---|---|---|
| 备份插件(定时 + 云存储) | 没有运维、内容每天更新、希望全自动 | 占 PHP 执行时间和内存,大库容易中途失败;插件本身出问题就没人备份 |
| 主机面板快照 | 整机级故障、误操作后快速回滚 | 粒度粗,只能整体回滚;快照存在同一家服务商 |
| mysqldump + cron | 有服务器权限、库超过 200MB | 需要自己写脚本、管轮转、验证 |
| 手工导出(phpMyAdmin / 后台工具) | 改配置、装插件前的临时打点 | 容易漏表、容易超时,不能当常规方案 |
实践中比较稳的组合是:插件或 cron 做每日自动备份到对象存储,人工在每次大改动前用 mysqldump 打一个点。自动的那份只管「有没有」,手动的那份保证「对不对」。备份插件的选择标准很简单——能不能把文件传到站外、能不能一键恢复、能不能设定保留份数。三条都满足就够了,功能再多也用不上。
备份文件不能放在网站根目录
这是最常见也最致命的一个错误。放在 /www/wwwroot/example.com/backup/ 下的 sql 文件,通过 https://域名/backup/xxx.sql 就能直接下载,而互联网上有一批扫描器专门跑常见备份路径的字典。一旦被下载,整个库的用户表、订单、后台账号哈希全部泄露。
正确的做法是放到 Web 根目录之外,比如 /home/backup/ 或挂载的独立数据盘,目录权限设成 700,属主是执行备份的用户。文件名也不要叫 backup.zip,带上站点名和精确到分钟的时间戳,既避免被猜中,也方便轮转脚本处理。
如果受限于虚拟主机只能放在站点目录内,至少做三件事:目录加 deny from all 或 Nginx 的 location ~* /backup/ { deny all; };文件名加随机串;把备份同步到站外后立刻删除本地副本。这三条只是补丁,不是方案,因为配置一旦在迁移或改版时丢失,文件就重新公开了。安全加固 检查表里应该包含这一项。
恢复演练:没演练过的备份等于没有
备份文件躺在那里不代表能用。压缩包可能只写了一半,dump 可能因为权限问题只导出了部分表,插件生成的包可能把文件路径写成了绝对路径。这些只有真正导入一次才知道。
- 准备一台测试机或临时子目录,不要在生产环境上试。
- 把最新的备份完整恢复:建库、导入 dump、解压 uploads、放回
wp-config.php。 - 用
wp search-replace 'https://正式域名' 'https://测试域名' --all-tables --precise换域名。 - 逐项检查:首页、文章页、图片、后台登录、表单提交、搜索结果、固定链接、以及任何一个有交互的功能(下单、留言、会员登录)。
- 记录耗时。超过 30 分钟就要优化流程,真出事时你没有那么多时间。
- 把这套步骤写成文档,写清库名、路径、命令,交给至少两个人。
演练频率建议每季度一次,以及每次大版本升级之后。WordPress 每年三个大版本,插件升级更频繁,版本更新 之后表结构可能变化,旧的 dump 恢复到新版本上有时需要额外跑一次升级。演练同时也能顺带发现 wp_options 或 wp_postmeta 里的历史垃圾,顺手做一次清理,可以让后续备份体积小一大截,具体方法在 数据库优化 里。
保留策略与轮转
备份不是越多越好。每天一份、留三年,磁盘会先撑不住,而且真要恢复时你只会用最近的那一份。按 3-2-1 原则定:至少 3 份副本,存在 2 种不同介质上,其中 1 份在异地。
- 日备保留 7 份,覆盖一周内的误操作。
- 周备保留 4 份,覆盖一个月内的内容问题。
- 月备保留 12 份,应对半年后才发现的篡改。
- 每次升级插件、改主题、改固定链接之前,额外手动打一个点,标注原因。
轮转用脚本加 find /home/backup -name "*.sql.gz" -mtime +30 -delete 就够了,别装额外的工具。关键是删除规则要跟保留策略一致,不然要么删太早,要么永远不删。
数据库备份这件事,做到「每日自动、站外一份、每季度真恢复一次」,就已经超过大多数企业站了。如果站点在淄博本地、或者不方便自己上服务器操作,信科网络可以代做一套备份加演练流程,含文档交付,电话 13280692153,邮箱 web@wordpressx.com。