数据库备份不是“复制粘贴文件”那么简单。很多人在数据丢失后才发现,之前做的备份根本恢复不出来,或者恢复出来的数据缺表、缺索引、缺存储过程。真正能落地的备份恢复,必须从备份类型、工具选择、恢复验证三个层面同时下手。备份策略里最核心的一条原则是:没有经过恢复演练的备份,等于没有备份。

先解决最基础的逻辑备份。MySQL的mysqldump是最常用的工具,但很多人用错了参数。默认情况下mysqldump不会备份触发器、事件和存储过程,必须显式加上--triggers --events --routines。一个完整的单库备份命令应该是:

mysqldump -u用户名 -p密码 --single-transaction --quick --lock-tables=false --triggers --events --routines 数据库名 > backup_$(date +%F).sql

这里--single-transaction针对InnoDB表保证一致性快照,--quick防止大表把内存撑爆。如果你的库里有MyISAM表,--single-transaction就不起作用了,需要换成--lock-tables或者直接锁全库。很多人线上备份时直接把服务卡死,就是因为没搞清楚存储引擎的区别。

恢复逻辑备份时最常见的错误是直接mysql 数据库名 < backup.sql。如果备份文件里没有包含建库语句,目标库不存在就会直接报错。正确的做法是先手动创建数据库并指定字符集,再导入。另外恢复大文件时建议先关闭binlog,否则恢复过程会产生大量二进制日志,拖慢速度甚至撑爆磁盘:

mysql -u用户名 -p密码 -e "SET GLOBAL sql_log_bin=0;"
mysql -u用户名 -p密码 数据库名 < backup.sql
mysql -u用户名 -p密码 -e "SET GLOBAL sql_log_bin=1;"

逻辑备份的优点是灵活、可读、跨版本兼容性好,但缺点是慢。数据量超过几十GB后,导出导入的时间会变得不可接受。这时候必须上物理备份。Percona XtraBackup是MySQL生态里最成熟的物理备份工具,支持在线热备份,不锁表,备份的是数据文件、redo log和undo log的物理副本。

XtraBackup完整备份的基本流程是:

xtrabackup --backup --target-dir=/backup/full --user=用户名 --password=密码 --parallel=4
xtrabackup --prepare --target-dir=/backup/full

注意--prepare这一步绝对不能省略。很多人备份完直接拿去恢复,结果恢复出来的库起不来,就是因为备份文件没有经过prepare,数据文件处于不一致状态。prepare的作用是应用备份期间产生的redo log,让数据文件达到一致状态。恢复时先把MySQL服务停掉,清空数据目录,然后执行:

xtrabackup --copy-back --target-dir=/backup/full
chown -R mysql:mysql /var/lib/mysql

物理备份的恢复速度比逻辑备份快一个数量级,但跨平台和跨版本兼容性差。MySQL 5.7的物理备份恢复到8.0大概率会出问题,大版本升级必须用逻辑备份或者专门的升级工具。

增量备份是控制备份窗口和存储成本的关键。XtraBackup的增量备份基于LSN(日志序列号),每次增量只备份自上次备份以来变化的页。完整增量链路的操作是:

# 全量备份
xtrabackup --backup --target-dir=/backup/full --user=用户名 --password=密码

# 第一次增量,基于全量
xtrabackup --backup --target-dir=/backup/inc1 --incremental-basedir=/backup/full --user=用户名 --password=密码

# 第二次增量,基于第一次增量
xtrabackup --backup --target-dir=/backup/inc2 --incremental-basedir=/backup/inc1 --user=用户名 --password=密码

恢复增量备份时必须先prepare全量,再依次apply增量,最后再整体prepare一次:

xtrabackup --prepare --apply-log-only --target-dir=/backup/full
xtrabackup --prepare --apply-log-only --target-dir=/backup/full --incremental-dir=/backup/inc1
xtrabackup --prepare --apply-log-only --target-dir=/backup/full --incremental-dir=/backup/inc2
xtrabackup --prepare --target-dir=/backup/full

这里--apply-log-only在中间步骤必须加上,它告诉工具不要做最后的回滚操作,否则后续增量无法继续应用。最后一步去掉--apply-log-only,完成最终的一致性恢复。

PostgreSQL的备份思路和MySQL有本质区别。PG的逻辑备份工具pg_dump同样支持自定义格式,恢复时用pg_restore可以并行恢复和选择性恢复对象。物理备份方面,PG官方推荐的是pg_basebackup配合WAL归档。pg_basebackup会复制整个数据目录并生成一个基础备份,之后通过持续归档WAL文件实现时间点恢复(PITR)。

PG的恢复配置文件postgresql.conf里需要设置:

archive_mode = on
archive_command = 'cp %p /backup/wal/%f'
wal_level = replica

基础备份命令:

pg_basebackup -D /backup/base -Ft -z -X fetch -U postgres

恢复时把基础备份解压到数据目录,然后在recovery.conf(PG12之前)或postgresql.auto.conf(PG12之后)里指定恢复目标。PG12之后的恢复配置直接写在postgresql.conf里:

restore_command = 'cp /backup/wal/%f %p'
recovery_target_time = '2025-01-15 10:30:00'
recovery_target_action = 'promote'

时间点恢复是PG物理备份最大的优势,可以精确恢复到误操作前的一秒。MySQL的binlog也能做类似的事,但操作复杂度更高,需要先恢复全量备份,再用mysqlbinlog解析二进制日志并指定stop-datetime。

SQL Server的备份体系相对封闭但功能完整。完整备份、差异备份、事务日志备份三层结构非常清晰。完整备份用BACKUP DATABASE,差异备份用BACKUP DATABASE WITH DIFFERENTIAL,日志备份用BACKUP LOG。恢复时必须按照完整备份→最后一次差异备份→后续日志备份的顺序依次恢复,并且中间步骤都要加NORECOVERY选项,最后一个日志备份才用RECOVERY让数据库上线。

不管用哪种数据库,备份策略的设计必须遵循几个硬性原则。第一,备份文件不能和数据库实例放在同一台物理机器上,否则磁盘故障会同时毁掉数据和备份。第二,至少保留一份异地备份,可以是对象存储、冷存储或者另一个机房的服务器。第三,备份文件要加密,特别是包含敏感数据的生产库。第四,定期做恢复演练,至少每月一次,把备份恢复到测试环境,验证数据完整性和恢复流程耗时。

恢复演练中暴露的问题往往比备份本身更致命。比如备份文件损坏、备份命令漏掉了关键对象、恢复脚本里的路径已经失效、恢复时间超过业务容忍上限。这些问题只有实际恢复一次才能发现。建议把恢复演练写成自动化脚本,定期在隔离环境执行,并记录恢复耗时、数据校验结果和遇到的问题。

自动化备份脚本的另一个关键是监控。备份任务失败必须能及时告警,而不是等到数据丢失才发现备份已经停了一周。监控项包括备份任务退出码、备份文件大小是否异常、备份耗时是否突然变长、备份文件数量是否符合保留策略。cron任务里任何非零退出码都应该触发告警。

数据备份恢复的完整实操,本质上是把“备份”从一个动作变成一个体系。这个体系包括备份策略设计、工具选型、命令执行、自动化调度、监控告警、恢复演练六个环节。任何一个环节缺失,都会在灾难发生时被无限放大。真正可靠的数据保护,不是看你做了多少次备份,而是看你成功恢复过多少次。