第 10 / 23 篇

第9章:误删演练必须保留错误已经扩散的事实

误删是恢复演练中最容易被低估的场景。数据库没有崩溃,监控可能全绿,事务甚至非常符合约束。若副本忠实跟随源库,错误也会迅速到达所有在线副本。此时“有一主一从”不能代替历史备份。本章沿着实际删除的传播过程,解释为什么恢复环境和当前服务环境必须分开处理。

错误事务同样会成功

实验删除所有订单和分录,再把余额设回初始值。这三项操作放在同一个事务里,最终账务关系仍然成立。它模拟的是操作意图错误,而非数据库部分提交。MySQL不能从SQL的合法性推断人是否选错了客户或漏写WHERE条件;复制协议也不会替业务识别这是一场事故。

删除之后再写一笔新订单,使当前源状态既不是空库,也不是事故前状态。若只检查表存在且有数据,就会误判恢复成功。我们的PITR目标拥有事故前的一百多笔订单,而当前源和复制追平后的副本只有事故后一笔。这两个状态都内部平衡,区别来自应该保留的业务集合。

历史恢复先进入隔离环境

事故发生时直接把生产库回退到旧状态,可能覆盖事故后合法订单,并触发应用重复执行支付、发货或通知。更稳妥的思路是先在隔离目标恢复历史状态,确认事故影响范围,再决定整库切换、按对象提取还是业务补偿。本书pitr就是这个隔离目标,不自动替代当前source。

恢复前保存事故现场也很重要。源上的误删后数据、应用日志和外部回执可能帮助区分需要保留的变化。立即清空源卷或重做复制,会删除修复线索。实验直到全部核对完成才销毁任务资源;生产应有更严格的只读取证和保留策略,避免操作人员为了尽快看到绿色状态而毁掉证据。

选边界需要业务上下文

本书事先知道错误事务发生的位置,便于精确展示。真实事故中,需要结合审计记录、业务ID、binlog事件和提交时间定位。时间戳适合缩小搜索范围,最后仍要落到事务边界。长事务可能在开始很久之后才提交,看到操作时间并不等于找到可回放终点。

如果误删与合法写入交织,简单回到误删前会排除一些后续合法交易。要保留它们,就进入选择性修复问题。应明确每笔修复的前置条件、幂等键和外部副作用,不能通过随意跳过DELETE事件得到一个看似完整的数据集。跨表依赖、唯一键占用和业务状态推进都可能使手工补入不再等价于原事务顺序。

复制追平是错误传播的证明

PITR完成后,程序重新启动源,等待副本执行源GTID集合,再逐行比较源与副本。副本只有事故后一笔订单,历史恢复实例则保留误删前目标。这个对照直接证明复制与备份服务于不同风险:复制提供较新的在线副本,历史恢复提供选择过去状态的能力。

这里没有说副本毫无价值。它可以降低单机故障后的恢复时间,也可分担读取。但当风险是合法提交的错误数据时,它通常会同步错误。延迟副本可以增加发现窗口,却引入主动落后与恢复操作复杂度,需要独立演练。本书的暂停SQL线程是机制实验,不等于实现了生产延迟备份策略。

从整库结果走向对象修复

对某个客户误删,可能只需要从隔离恢复库提取该客户的完整业务对象。提取范围不应局限于一张主表,还需包含分录、状态历史、关联索引和外部凭证。导回当前库前先分析同一ID是否已有后续记录,明确冲突如何处理,执行后再次核对业务关系。

对整库回退,则需要更严格的写入冻结和入口切换,因为数据库回到过去,应用缓存、搜索索引、消息消费位置和外部系统未必同步回退。恢复数据库只是系统恢复的一部分。应让各下游知道新的数据版本或修复范围,避免旧缓存把已恢复的数据再次覆盖。

记录被排除的内容

报告不能只有“保留了二千零一订单”。还应明确删除事务没有执行,误删后订单三千零一没有进入目标。排除集是恢复目标的一半。若只检查保留集,结束位置太晚时可能保留订单又把它删除,或者额外带入不该出现的变更。完整状态摘要与显式排除查询一起缩小这种漏洞。

演练结束时写清事故发现时间、最早受影响事务、选择边界依据、恢复输入、验证差异和剩余修复工作。客户补偿与财务确认可能晚于技术恢复,这不是可以省略的尾巴。恢复报告应让业务知道当前系统承认哪些事实,以及哪些事实仍需人工处理。