
备份的目标是在约定时间内,把数据恢复到约定的时间点。本文以 MySQL 8.4 和以 InnoDB 为主的数据库为例,介绍备份选择、逻辑备份、二进制日志归档、时间点恢复和恢复演练。示例中的 appdb、账户、路径和日志位置均需按实际环境替换;恢复命令应先在隔离实例演练。
一、先确定恢复目标
- RPO(恢复点目标):最多允许丢失多长时间的数据。例如 RPO 为 15 分钟,就需要保证备份和日志归档能把数据恢复到故障前约 15 分钟以内。
- RTO(恢复时间目标):从故障发生到服务恢复可用,最多允许多久。数据量大时,逻辑备份的导入速度往往比导出速度更需要关注。
- 恢复范围:是误删一张表、损坏一个库,还是整台实例不可用?单库 SQL 文件适合按库恢复;实例级故障还需要配置、账号、证书、插件、二进制日志等信息。
- 保留期:至少覆盖发现错误所需的时间。全量备份和二进制日志必须形成连续链,不能只保留最近一个 SQL 文件。
先写下 RPO、RTO 和恢复范围,再决定备份频率与工具。复制一份文件只是备份动作;只有恢复演练成功,才能证明方案可用。
二、几种备份方式怎么选
| 方式 | 保存的内容 | 适用场景 | 主要限制 |
|---|---|---|---|
| 逻辑备份 | 建库建表语句、数据、视图和可选的存储对象 | 小中型数据库、按库迁移、检查或选择性恢复 | 大库导出和重建索引可能很慢;不含服务器配置与二进制日志 |
| 物理备份 | 数据文件及工具要求的相关文件 | 大库、对恢复速度要求较高的实例 | 依赖工具、存储引擎和版本兼容性;不能直接复制运行中的 InnoDB 数据目录充当一致备份 |
| 二进制日志归档 | 全量备份后的变更事件 | 把全量备份推进到指定时间点 | 单独的日志通常不能替代基线全量备份,日志缺一段就无法连续回放 |
| 文件系统快照 | 某一时刻的文件系统状态 | 配合数据库一致性措施做快速快照 | 普通快照不自动保证 MySQL 内部一致性,也不能代替异地副本 |
全量备份保存一个完整基线;增量备份只保存相对上次备份的变化;差异备份保存相对上次全量备份的变化。物理备份工具可支持页级增量;MySQL 二进制日志则可记录基线之后的变更,用于时间点恢复。两者不是同一种文件格式,恢复步骤也不同。
一个可落地的例子是:每天一次全量备份,连续归档二进制日志,保留至少两轮完整备份链,并把副本放在另一台设备或对象存储中。具体间隔要由 RPO、RTO、数据量和恢复演练结果决定。备份文件应加密,密钥与备份分开保管;限制读取权限,避免把数据库密码写在命令行或备份脚本中。
三、备份前检查
先确认版本、存储引擎、二进制日志和保留期:
SELECT VERSION();SELECT @@GLOBAL.log_bin, @@GLOBAL.gtid_mode, @@GLOBAL.binlog_expire_logs_seconds;SELECT TABLE_NAME, ENGINEFROM information_schema.TABLESWHERE TABLE_SCHEMA = 'appdb' AND TABLE_TYPE = 'BASE TABLE';SHOW BINARY LOGS;log_bin 为 ON 才能按下文做二进制日志时间点恢复。MySQL 8.4 默认启用二进制日志,但仍应检查实际实例配置。binlog_expire_logs_seconds 必须长于需要保留的恢复窗口,并留出备份、传输及故障发现的余量;如果日志在归档前被清理,就会出现恢复缺口。
检查是否存在 MyISAM、MEMORY 等非事务表。mysqldump --single-transaction 只保证 InnoDB 等事务表在同一一致性快照中导出;它不能让非事务表自动变成一致快照。备份期间也不要对待导出的表执行 ALTER TABLE、CREATE TABLE、DROP TABLE、RENAME TABLE 或 TRUNCATE TABLE,否则导出可能失败或内容不正确。
备份账户需要与导出对象和选项相符的权限。例如表需要 SELECT,视图需要 SHOW VIEW,触发器需要 TRIGGER,事件需要 EVENT;--routines 和记录日志坐标的 --source-data 还涉及额外权限。不要为了省事长期使用管理员账户执行定时备份。
四、用 mysqldump 做单库逻辑备份
下面的 PowerShell 示例导出 appdb,并把 SQL 写到 D:/mysql-backup/appdb-full.sql。先确保备份目录存在:
New-Item -ItemType Directory -Force -Path 'D:/mysql-backup'mysqldump --host=127.0.0.1 --user=backup_user --password --databases appdb --single-transaction --quick --routines --events --triggers --hex-blob --no-tablespaces --default-character-set=utf8mb4 --result-file='D:/mysql-backup/appdb-full.sql'if ($LASTEXITCODE -ne 0) { throw 'mysqldump failed' }Get-FileHash -Algorithm SHA256 'D:/mysql-backup/appdb-full.sql'--password 不附带密码值时会交互式提示输入。--databases appdb 会在文件中包含建库与 USE 语句;--routines、--events、--triggers 分别覆盖存储程序、事件和触发器;--hex-blob 使二进制列以十六进制形式导出;--no-tablespaces 省略表空间 DDL。--quick 按行从服务器读取,适合较大表。正式脚本还应记录开始与结束时间、退出码、文件大小和校验值,并在失败时通知管理员。
Windows PowerShell 的 > 重定向可能把转储写成 UTF-16,导致 MySQL 无法正确导入,因此这里使用 --result-file。此选项会覆盖同名文件,即使导出途中报错也可能留下文件;文件存在或哈希算得出来,并不等于备份成功。应同时检查退出码并定期做完整恢复演练。
GTID 注意:
mysqldump默认的--set-gtid-purged=AUTO在启用 GTID 时可能把源实例所有已执行事务的 GTID 写入导出文件,即使只导出了appdb。向已有实例导入局部备份、搭建复制或连续导入多个局部备份前,必须检查该语句和目标实例的 GTID 状态。--set-gtid-purged=OFF适用于明确不需要保留该 GTID 集的独立数据副本,不能机械地用于所有恢复场景。
恢复单库 SQL 文件
先在隔离的空实例测试。下例的转储包含 CREATE DATABASE 和 USE,因此命令不必额外指定数据库:
mysql --host=127.0.0.1 --user=restore_user --password --execute='SOURCE D:/mysql-backup/appdb-full.sql'if ($LASTEXITCODE -ne 0) { throw 'restore failed' }不要在生产实例上直接试运行:转储可能包含 DROP TABLE、CREATE TABLE 和大量 INSERT。导入后至少检查表、关键记录数、视图、存储程序、触发器和事件,再运行应用的关键读写流程。可以先用以下查询查看对象是否恢复:
SHOW FULL TABLES FROM appdb;SELECT ROUTINE_NAME, ROUTINE_TYPEFROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = 'appdb';SELECT TRIGGER_NAMEFROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA = 'appdb';SELECT EVENT_NAMEFROM information_schema.EVENTS WHERE EVENT_SCHEMA = 'appdb';记录原库与恢复库的关键业务表行数、抽样记录和应用校验结果。行数一致不能证明全部内容正确,但能发现明显漏表或漏数据。
五、全量备份加二进制日志:时间点恢复
误删数据往往在下一次全量备份前才被发现。要恢复到误操作之前,需要一份一致的基线、基线对应的二进制日志坐标,以及从该坐标到目标时间点之间连续且未损坏的日志文件。
以下示例假设源实例只承载 appdb,恢复目标是隔离的空实例。多库实例应设计实例级一致备份和全实例日志回放,不能把单库转储与全实例日志直接拼接后当作通用恢复方案。
1. 记录与备份同一时刻的日志坐标
在上面的 mysqldump 命令中额外加入 --source-data=2,并另存为一份用于时间点恢复的基线:
mysqldump --host=127.0.0.1 --user=backup_user --password --databases appdb --single-transaction --quick --routines --events --triggers --hex-blob --no-tablespaces --source-data=2 --result-file='D:/mysql-backup/appdb-pitr-base.sql'if ($LASTEXITCODE -ne 0) { throw 'baseline dump failed' }文件内会有一条被注释掉的 CHANGE REPLICATION SOURCE TO 语句,记录类似 SOURCE_LOG_FILE='binlog.000123', SOURCE_LOG_POS=154 的坐标。=2 让它只作记录,不会在导入时真的执行复制配置。该选项要求二进制日志已开启和相应权限;它在开始导出时可能短暂获取全局读锁。保持备份期间没有上述 DDL。
2. 保存完整的日志链
SHOW BINARY LOGS 列出当前日志文件。将基线坐标所在的文件以及之后所有需要的文件安全地归档到另一处,持续监控归档是否中断。MySQL 也支持用 mysqlbinlog --read-from-remote-server --raw --stop-never 连续拉取原始二进制日志;该进程断线后不会自动重连,必须由服务管理和监控负责重启与检查缺口。
不要在确认归档、复制从库需求和恢复窗口之前清理二进制日志。全量 SQL 文件、二进制日志、版本与配置记录应一起管理;仅有 SQL 文件无法恢复其导出之后的事务。
3. 在隔离实例回放到正确位置
先导入 appdb-pitr-base.sql。然后用 mysqlbinlog 检查日志,找出误操作事务开始前的事件边界,确认目标日志文件及位置。相比只按墙上时钟截断,按文件和事件位置更明确;不要把停止位置设在一个事务的中间。
假设基线坐标为 binlog.000123:154,误操作从 binlog.000124:8876 开始,可在 PowerShell 中先生成回放文件,再用同一个 MySQL 客户端连接导入:
mysqlbinlog --start-position=154 --stop-position=8876 --result-file='D:/mysql-backup/replay.sql' 'D:/mysql-backup/binlog.000123' 'D:/mysql-backup/binlog.000124'if ($LASTEXITCODE -ne 0) { throw 'mysqlbinlog failed' }mysql --binary-mode --host=127.0.0.1 --user=restore_user --password --execute='SOURCE D:/mysql-backup/replay.sql'if ($LASTEXITCODE -ne 0) { throw 'binlog replay failed' }--start-position 作用于列出的第一个日志文件,--stop-position 作用于最后一个。中间日志按顺序完整处理。一次生成回放文件并通过一次客户端连接导入,能避免跨日志文件的会话状态被拆断。这里使用 mysqlbinlog --result-file,避免 PowerShell 文本重定向改变文件编码。回放账户还需要执行日志事件及内部 BINLOG 语句所需的权限。
最后核对目标事务之前的数据、对象定义和应用行为。确认结果后,再按业务切换流程恢复服务;不要在未经验证的情况下覆盖原始实例。
六、物理备份和大库的选择
当 SQL 导入时间达不到 RTO 时,考虑物理备份。MySQL Enterprise Backup 的 mysqlbackup 支持 InnoDB 在线物理备份与增量备份;MySQL Shell 的 dump/load 工具则提供并行的逻辑导出与导入。选择前应测量真实数据量下的备份时间、恢复时间、磁盘占用和对业务的影响。
直接压缩或复制正在运行的 InnoDB 数据目录,不能代替了解事务与日志的一致备份工具。停机文件备份需要在服务器完全停止后保存完整数据目录及恢复所需的配置与日志,并在兼容的 MySQL 环境中演练。不要只复制某个 .ibd 文件就认为已经备份了整张表。
七、自动化和恢复演练清单
- 每次备份记录版本、实例标识、范围、开始和结束时间、退出码、文件大小、哈希、基线日志坐标,以及备份文件存放位置。
- 自动检查最近一次成功备份的时间、日志归档连续性、剩余空间和保留期;失败要主动告警。备份文件至少保留一份与数据库主机分离的副本。
- 定期在隔离实例从备份完整恢复,再回放日志到选定时间点,测量实际 RTO,并检查关键业务数据和应用启动。恢复演练不能只做
SQL文件语法检查。 - 轮换与限制备份账户;交互输入密码或使用受保护的客户端配置,避免把明文密码写入命令行、脚本、日志和仓库。控制备份文件及解密密钥的访问权限。
- 清理旧备份时,以整条可恢复链为单位:保留对应全量备份和从其坐标开始的全部必要日志。先确认副本与恢复演练,再清理过期文件。
常见误区
| 做法 | 问题 |
|---|---|
只复制正在运行的 .ibd 或数据目录 | 文件之间可能不是同一一致状态,恢复还依赖数据字典、日志和其他文件 |
| 只有全量 SQL 文件,没有后续二进制日志 | 最多只能回到导出时刻 |
使用 --single-transaction 就认为所有表都一致 | 非事务表不受该快照保护;备份期间的 DDL 也会破坏结果 |
| 只看备份文件存在或哈希正常 | 不能证明 SQL 可导入、对象齐全或应用可运行 |
PowerShell 中用 > 写 mysqldump 输出 | 可能得到 UTF-16 文件,导入失败 |
| 日志自动过期却没有归档监控 | 恢复链可能已经断裂 |
| 把单库备份的 GTID 语句直接导入已有复制拓扑 | 可能污染或冲突目标实例的 GTID 集 |
参考资料
分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
MySQL 学习笔记
连载中最后更新于 ,距今已过 1 天
部分内容可能已过时
评论