第 6 / 23 篇

第5章:在真实写入期间取得一致快照

暂停全部业务后导出数据库容易理解,但许多系统无法每天长时间停写。在线逻辑备份必须回答一个微妙问题:订单表和账户表在不同时间被读出,如何仍然属于同一个业务时刻?本章用持续提交的三表事务检验single-transaction的实际结果,而不只检查命令是否带了这个选项。

构造会暴露不一致的业务

实验初始写入一笔金额一百的订单,账户余额变成九万九千九百。随后写入器连续提交一百二十笔金额为一的订单。每次提交同时改变订单、分录和余额,事务之间短暂停顿。备份如果把余额读在一个时点、分录读在另一个时点,就可能违反余额关系;如果订单和分录读取不一致,也会出现孤立对象。

填充表存放约十几MB文本,使导出有足够时间与写入重叠。它不参与账务摘要。选择填充而不是把并发写入改成巨量业务,是为了保持输出可读,也避免读者把测试规模误认为容量基准。关键不是导出花了几秒,而是在实际导出时间范围内确实有其他事务提交。

程序等待写入器至少新增一笔,再记录源订单数量,开始dump,完成后再查一次源数量。必须满足导出后的数量大于导出前的数量。写入器还需正常完成,最终源数量必须大于导出后数量。恢复快照的订单数应落在导出前后观测之间,同时所有账务关系成立。这个检查同时证明并发发生和恢复状态一致。

single-transaction的适用范围

mysqldump以一致快照读取事务表,使多个表的数据读取可以对应同一视图。我们显式使用InnoDB,不做并发DDL。这个约束很重要:MyISAM等非事务表不因选项出现就获得相同保证,备份期间的ALTER、DROP或RENAME也可能破坏原来的假设。官方文档R02专门说明了相关限制。

source-data记录与备份关联的复制坐标。single-transaction和source-data组合可能在起始阶段使用短暂锁来协调坐标,不意味着整个导出期间完全没有锁影响。因此“在线备份”不应被写成“绝对不影响写入”。实验观测到持续提交,只能说明此数据集与环境里写入确实继续,不能证明生产峰值延迟没有变化。

GTID选项要服务于目标

本书设置set-gtid-purged=OFF,避免把整个源服务器执行历史声明给独立教学恢复实例。恢复目标是验证业务数据与回放效果,不是从该dump直接构建原拓扑副本。若要用备份初始化生产GTID副本,必须重新设计GTID集合的传递、空实例条件和重复事务处理,不能只复制这一组选项。

逻辑备份可能只导出一个数据库,而源GTID历史覆盖服务器上更多数据库。把全局集合和局部数据混合使用,可能让恢复端以为一些实际上未导入的事务已经执行。这类错误很隐蔽:复制自动定位能正常启动,却永远不会再补齐被错误声明的事务。数据范围与事务历史范围必须一起审查。

真正导入后再判断一致性

dump文本里的INSERT数量不能代替恢复测试。程序把字节输入独立restore实例,等待mysql客户端成功,再通过state函数查询余额、分录和有序订单。快照比源最终状态少一些订单是预期结果,因为源在备份后继续提交。不能因为两者摘要不同就说备份坏了,也不能因内部一致就说所有最新事务都已保护。

恢复报告记录导出持续时间、前后源计数、恢复计数和dump摘要。读者可以看到快照位于观测窗口内,而不是把一个事后生成的静态快照说成并发快照。这个做法也适用于测试其他备份工具:先设计能暴露跨表错位的不变量,再在真实变化期间取备份,最后到另一环境验证。

备份窗口之外的维护

长事务会保留旧版本,备份本身也可能延长undo保留和增加I/O。表越大、写入越重,这些效应越值得观测。应在容量演练中记录业务延迟、历史版本与磁盘增长,并确定备份失败的终止策略。本书没有把小规模观察外推成无成本方案,而是把正确性验证与性能评估保留为两项独立任务。