编写 schema 变更与回滚脚本
目标
使用扩展—迁移—收缩模式变更 schema,并建立一整套变更管理方案,包括回滚脚本、分批回填、验证脚本和操作手册。
为什么重要
‘有 down 脚本,所以很安全’是最常见的误解。
用于撤销 DROP COLUMN 的 down 脚本只能恢复列结构,无法找回列中的值。
可逆性不是代码自身的属性,而是代码、数据和时间三者共同作用的结果;
同一项变更若昨天刚部署,可能还可逆,但积累一百万条数据后就可能不可逆。
此外,若把大规模回填放在一个事务中执行,锁和日志量会急剧增加,
导致只读副本落后,查询服务向用户展示过期数据。
本实验将让你亲手练习如何避开这些陷阱。
步骤
- 准备:需要
dbo-schema实验中的/root/db/si.db。 如果不存在,可以使用/opt/lab/fixtures/dbo/si-base.sql创建。 - 将当前 schema 保存为
/root/db/schema_v1.sql, 并创建/root/db/baseline.txt。文件包含三行:orders_cnt=<ORDERS 행 수> orders_amt=<ORDERS 의 ORD_AMT 합계> item_qty=<ORDER_ITEM 의 QTY 합계> - 创建
/root/db/changelog.csv。首行为chg_id,date,object,ddl_file,rollback_file,est_sec,approver。 预先登记第 3~5 步将要创建的 3 项变更。ddl_file、rollback_file、approver都不能为空,est_sec必须填写数字。 - 创建
/root/db/mig/V2__add_dlvr_sts.sql。- 在
ORDERS中添加DLVR_STS_CD列(默认值为'01') - 将所有现有行填充为
'01'应用后,DLVR_STS_CD为 NULL 的行数必须为 0。
- 在
- 创建
/root/db/mig/V2__rollback.sql。 应用后,schema 必须与schema_v1.sql相同。 (请在si.db的副本上执行 up → down 进行确认。) - 创建
/root/db/mig/V3__item_qty_check.sql。 将ORDER_ITEM.QTY的 CHECK 约束强化为不小于 1 且不大于 9999。 sqlite 不支持直接修改约束,因此必须执行表重建流程。 应用后,ORDER_ITEM的行数和QTY总和仍须与第 1 步基线一致, 外键关系也必须保留。 - 创建
/root/db/batch-update.sh。脚本接收两个参数(DB파일 배치크기), 将ORDERS中DLVR_STS_CD为'01'的行改为'02', 但要每次只处理指定的批量大小并循环执行。 最后一行输出batches=<반복횟수> updated=<총건수>。 (为避免污染原始数据库,只能修改通过参数传入的 DB。) - 创建
/root/db/mig-verify.sh。脚本接收两个参数(기준선파일 DB파일), 将基线中的三项指标与当前 DB 进行比较。 若全部一致,第一行输出OK;若不一致,则输出以NG开头的行, 并指出哪些指标不同。两种情况的退出码分别为 0 和非 0。 - 编写
/root/db/mig-runbook.md。 必须包含## 작업 개요、## 사전 백업、## 적용 절차、## 검증、## 롤백 기준、## 롤백 절차六个 h2 标题。 正文中必须包含备份文件路径,以及用数字表示的回滚标准(耗时或验证失败数量)。
参考
- 导出 schema:
sqlite3 /root/db/si.db .schema > schema_v1.sql - 表重建顺序:
PRAGMA foreign_keys=off→ 创建新表 →INSERT ... SELECT→ DROP 旧表 → RENAME → 重建索引 →PRAGMA foreign_keys=on - 受影响行数:
SELECT changes(); - 常见错误 1:不创建副本,直接在原始数据库中测试,最终无法恢复。
- 常见错误 2:重建表时没有重新创建索引。 DROP 表时,索引也会一并消失。
- 常见错误 3:分批回填循环没有结束条件,导致无限循环。 当受影响行数为 0 时必须停止。
Schema 快照与基线
将当前 schema 保存为 /root/db/schema_v1.sql,
并创建 /root/db/baseline.txt。文件包含三行:
orders_cnt=<ORDERS 행 수>
orders_amt=<ORDERS 의 ORD_AMT 합계>
item_qty=<ORDER_ITEM 의 QTY 합계>
等工作完成后才问‘原来有多少条?’就已经太晚了。请同时保留 schema 和数据指标。
变更管理台账
创建 /root/db/changelog.csv。首行为
chg_id,date,object,ddl_file,rollback_file,est_sec,approver。
预先登记第 3~5 步将要创建的 3 项变更。
ddl_file、rollback_file、approver 都不能为空,
est_sec 必须填写数字。
将回滚脚本路径设为必填项,可以让你在填写台账时、而不是部署后,发现‘这项变更无法回滚’。
扩展脚本
创建 /root/db/mig/V2__add_dlvr_sts.sql。
- 在
ORDERS中添加DLVR_STS_CD列(默认值为'01') - 将所有现有行填充为
'01'应用后,DLVR_STS_CD为 NULL 的行数必须为 0。
先添加列,再填充现有行。请思考先以允许 NULL 的方式添加并回填,与一开始就添加为 NOT NULL 有何区别。
回滚脚本
创建 /root/db/mig/V2__rollback.sql。
应用后,schema 必须与 schema_v1.sql 相同。
(请在 si.db 的副本上执行 up → down 进行确认。)
必须能够确认回滚后的 schema 与原始 schema 相同。比较快照是最可靠的方法。
表重建迁移
创建 /root/db/mig/V3__item_qty_check.sql。
将 ORDER_ITEM.QTY 的 CHECK 约束强化为不小于 1 且不大于 9999。
sqlite 不支持直接修改约束,因此必须执行表重建流程。
应用后,ORDER_ITEM 的行数和 QTY 总和仍须与第 1 步基线一致,
外键关系也必须保留。
修改约束需要创建新表并迁移数据。若不遵守正确顺序,数据或引用关系会损坏。
分批回填
创建 /root/db/batch-update.sh。脚本接收两个参数(DB파일 배치크기),
将 ORDERS 中 DLVR_STS_CD 为 '01' 的行改为 '02',
但要每次只处理指定的批量大小并循环执行。
最后一行输出 batches=<반복횟수> updated=<총건수>。
(为避免污染原始数据库,只能修改通过参数传入的 DB。)
在一个事务中执行大规模 UPDATE 会使锁和日志量急剧增加。请设计成反复执行,直到受影响行数变为 0。
迁移验证脚本
创建 /root/db/mig-verify.sh。脚本接收两个参数(기준선파일 DB파일),
将基线中的三项指标与当前 DB 进行比较。
若全部一致,第一行输出 OK;若不一致,则输出以 NG 开头的行,
并指出哪些指标不同。两种情况的退出码分别为 0 和非 0。
只看行数和总和,无法发现‘一条缺失、另一条翻倍’的情况。请使用多个指标。
操作手册
编写 /root/db/mig-runbook.md。
必须包含 ## 작업 개요、## 사전 백업、## 적용 절차、## 검증、
## 롤백 기준、## 롤백 절차 六个 h2 标题。
正文中必须包含备份文件路径,以及用数字表示的回滚标准(耗时或验证失败数量)。
这是需要在凌晨阅读的文档。命令应能够直接复制执行,并且必须用数字写清何时停止和回滚。