TT Lab
价格 开始
学习 学习路径 课程

不可撤销的变更

安全地改动生产数据

在 TT Lab 中继续学习

目标

将客户用自然语言提出的变更请求收窄为明确条件,准备回滚路径,在副本上演练回滚,再完成正式应用和对照组验证的完整流程。

为什么重要

在之前的课程中,你掌握的权限实际上只有读取。错误的查询可以重新执行,错误的假设可以删除后重新建立。写入则不同。错误的 UPDATE 能回滚到什么程度,完全取决于事先准备了多少回滚手段。

本练习强制执行的四点都是实际现场规则。将请求语句转成条件前先计数——请求者想象的范围与实际变更范围会出现偏差,在计数前双方都不知道差距有多大。先准备回滚路径——文件副本用于整体回滚,行快照用于说明哪些内容如何发生了变化,因此两者都需要。不能只在口头上说可以回滚——回滚失败通常源于小问题,但如果在正式应用后才发现,这个小问题就不再小。同时统计不应发生变化的对象——没有对照组的验证,即使误改了相邻数据也能通过。

本次请求如下。客户运维负责人以结算截止为由,发来“请将所有 pending 订单改为 cancelled”的请求。基准日期为 2026-08-29。但这 45 笔 pending 订单并非同一类:有些已停滞很久,有些昨天才进入、可能仍在处理中,还有些无法确认客户身份。

本练习使用两个条件收窄范围:创建日期早于 2026-08-15(不包含当天),并且该订单的 customer_id 存在于 customers 表中。

步骤

  1. 创建 /root/change,将 /opt/data/support.sql 导入为 /root/change/ops.db。从工作副本开始,而不是直接处理客户原始数据。
  2. 在 /root/change/scope.txt 中写四行。request_total= 是按请求原样转换后会更改的全部 pending 数量;target= 是按上述两个条件收窄后的数量;excluded_recent= 是创建日期在 2026-08-15 之后的 pending 数量;excluded_orphan= 是客户不在 customers 表中的较早 pending 数量。
  3. 创建文件副本 /root/change/ops.db.bak,并将目标行变更前的值输出到 /root/change/backup.csv,表头为 id,customer_id,amount,status,created_at。
  4. 在 /root/change/plan.md 中编写计划。需要 ## 요청、## 대상과 범위、## 영향、## 되돌리는 방법、## 검증 방법、## 중단 기준 六个章节,而且 ## 대상과 범위 必须位于 ## 되돌리는 방법 之前。
  5. 在事务中执行 UPDATE,读取 SELECT changes(),然后执行 ROLLBACK。在 /root/change/dryrun.txt 中写入 changes=,以及回滚后重新统计的 after_rollback_pending=。
  6. 创建 /root/change/rehearsal.db,其内容来自 ops.db.bak,应用相同变更,再使用 backup.csv 恢复。在 /root/change/rehearsal.txt 中写入 applied=、restored_pending=,以及将恢复后的数据库与 ops.db.bak 逐行比较得到的 diff=。
  7. 使用相同条件对 ops.db 正式应用变更,并在 /root/change/applied.txt 中记录 changed=。
  8. 在 /root/change/verify.txt 中写四行:backup_ids_cancelled=、control_pending=、paid_unchanged=、refunded_unchanged=;在 plan.md 末尾追加 ## 적용 결과 章节,记录验证所得数字。

参考

创建工作副本

创建 /root/change,将 /opt/data/support.sql 导入为 /root/change/ops.db。从工作副本开始,而不是直接处理客户原始数据。

客户提供的快照是 /opt/data/support.sql。sqlite3 通过标准输入接收 SQL——如果作为参数传递,只会留下一个空数据库。

统计请求范围与实际范围的差异

在 /root/change/scope.txt 中写四行。request_total= 是按请求原样转换后会更改的全部 pending 数量;target= 是按上述两个条件收窄后的数量;excluded_recent= 是创建日期在 2026-08-15 之后的 pending 数量;excluded_orphan= 是客户不在 customers 表中的较早 pending 数量。

分别使用 COUNT 统计按请求原样转换的条件和收窄后的条件。收窄条件有两个——创建日期,以及该客户是否存在于 customers 中。

建立可回滚状态

创建文件副本 /root/change/ops.db.bak,并将目标行变更前的值输出到 /root/change/backup.csv,表头为 id,customer_id,amount,status,created_at。

使用 cp 创建文件副本,使用 sqlite3 的 .mode csv 创建行快照。快照条件必须与第 2 步收窄后的条件相同。

编写变更计划

在 /root/change/plan.md 中编写计划。需要 ## 요청、## 대상과 범위、## 영향、## 되돌리는 방법、## 검증 방법、## 중단 기준 六个章节,而且 ## 대상과 범위 必须位于 ## 되돌리는 방법 之前。

需要六个章节。“回滚方法”章节必须写明使用什么文件回滚,包括文件路径;“中止标准”章节用于说明看到什么结果时应停止。

不提交地进行试运行

在事务中执行 UPDATE,读取 SELECT changes(),然后执行 ROLLBACK。在 /root/change/dryrun.txt 中写入 changes=,以及回滚后重新统计的 after_rollback_pending=。

使用 BEGIN 开启事务,执行 UPDATE 后用 SELECT changes() 读取受影响的行数,再用 ROLLBACK 结束。写下“已回滚”和实际完成回滚是两回事,因此必须重新计数。

演练回滚

创建 /root/change/rehearsal.db,其内容来自 ops.db.bak,应用相同变更,再使用 backup.csv 恢复。在 /root/change/rehearsal.txt 中写入 applied=、restored_pending=,以及将恢复后的数据库与 ops.db.bak 逐行比较得到的 diff=。

在单独副本上操作,而不是在生产副本上操作。回滚时不要重新编写条件,要读取 backup.csv 进行恢复——真实事故中手上只有该文件。

使用收窄后的条件正式应用

使用相同条件对 ops.db 正式应用变更,并在 /root/change/applied.txt 中记录 changed=。

使用与第 5 步试运行时逐字相同的条件。如果此时想修改条件,就意味着必须重新进行试运行。

连同对照组一起统计验证

在 /root/change/verify.txt 中写四行:backup_ids_cancelled=、control_pending=、paid_unchanged=、refunded_unchanged=;在 plan.md 末尾追加 ## 적용 결과 章节,记录验证所得数字。

同时统计应发生变化和不应发生变化的对象。计划只有记录结果后才算闭环——请追加“应用结果”章节。