目标
损坏发生后每多写一分钟,修复集就扩大一分,坏值就多渗进一批衍生存储。冻结写入、立即取证快照、用日志圈定损坏窗口,把无界的紧急事变成有限的带证据修复工程;修复要逐行记录前后值,衍生存储逐一清洗,解冻才算真正结束。
步骤要点
- 冻结受影响数据的写入(特性开关或只读模式),并立即做一份独立于修复现场的取证快照。
- 用审计日志、复制延迟历史与备份校验确定首个与最后一个损坏写入,产出“满足谓词 X 且写于 T1 到 T2”的行级窗口。
- 量化影响半径:统计受影响行数、列出下游消费者与吞过坏值的衍生数据(缓存、搜索索引、导出)。
- 从损坏前备份恢复或重算正确值,修复脚本逐行记录修改前后值,抽样独立验证。
- 渐进解冻写入并重放排队合法写入,逐一清洗或重建含坏值的衍生存储。
排错提示
直接边修边覆盖毁掉了证据,事后复盘与审计都需要那份取证快照,先快照后修复;数据库修好了搜索结果仍错是衍生层漏清,影响半径清单必须覆盖每个衍生消费者;窗口圈定靠猜而没对日志,修复集就会漏行,用审计日志加备份校验交叉验证边界。
适用边界
本流程依赖审计日志与 PITR 能力,缺失这两样的系统要先补观测再谈修复;行级验证只能抽样与谓词全查结合,极端规模的逐行人工核对不现实。
代码示例
INC-2950 orders.total_cents corrupted by v1.8.2
14:12Z FREEZE writes on orders (flag orders-ro=on)
14:14Z forensic snapshot: orders_corrupt_20261022.pgdump
window (from audit log):
first bad row: 13:58:41Z (deploy v1.8.2 at 13:57)
last bad row: 14:11:59Z
predicate: total_cents = round(subtotal * 1.0)
count: 3,842 rows
blast radius: search index, nightly export, cache
repair: recompute from line_items, log per row
14:52Z 3,842 rows repaired, 50 sampled OK
unfreeze: 15:03Z gradual ramp
purge: search reindex 15:10Z, export re-run 15:40Z