LabHub
学习 学习路径 课程

SI 的数据库运维

可以撤销的 schema 变更

在 LabHub 中继续学习

一句话总结

可逆性不是代码自身的属性,而是代码、数据与时间的组合。同一项变更,在无人使用时可以回滚;积累一百万条数据后就可能无法回滚。

概念图: 代码、数据与时间的组合 · 无法找回列中原有的值 · 可逆性是代码、数据与时间的组合。 · “有 down 脚本,所以安全”这一误解

为什么这是个问题

“有 down 脚本,所以安全”很危险。用于回滚 DROP COLUMN 的 down 只能重建列结构,无法找回列中原有的值。schema 回来了,数据却不会回来。

所以制定回滚计划时,不应只问“有没有回滚脚本”,而应问“回滚后会失去什么”。如果在部署前提出这个问题,通常会选择把变更拆成扩展 → 迁移 → 收缩。

可逆性不是代码的属性

“这项变更可以回滚吗?”准确答案如下。

可逆性是代码、数据与时间的组合。 同一个代码变更,尚无人使用时是可逆的; 积累一百万条数据后,就会变得不可逆。

新增一列能否回滚?昨天刚部署且无人使用时可以。一周内已有一百万条记录写入该列后,DROP 就是在丢弃一百万份数据。schema 能回退,数据不能。

“有 down 脚本,所以安全”这一误解正源于此。回滚 DROP COLUMN 的 down 只能恢复列结构,无法恢复值。因此有句话说:down migration 大多是谎言。

危险 DDL 清单

DDL 为什么危险
添加 NOT NULL 为检查所有行而长时间锁表;存在 NULL 就失败
列改名 看似原子操作,本质上相当于删除 + 新增;旧版代码会立即损坏
类型变更 部分 DBMS 会重写整张表,大表耗时很长
创建索引 大表可能产生锁或负载,需确认在线选项
大规模回填 UPDATE 巨型事务导致锁与复制延迟,只读副本可能崩溃
DROP COLUMN 数据消失,无法回滚

尤其要小心大规模回填。一次事务 UPDATE 五百万行,会让事务日志暴增并产生复制延迟。如果查询流量走副本,查询服务此时会展示旧数据。因此,回填必须分批。

-- 나쁜 예
UPDATE ORDERS SET DLVR_STS = '01' WHERE DLVR_STS IS NULL;

-- 좋은 예: 1000건씩, 사이에 잠깐 쉬면서
UPDATE ORDERS SET DLVR_STS = '01'
 WHERE ORD_NO IN (SELECT ORD_NO FROM ORDERS WHERE DLVR_STS IS NULL LIMIT 1000);
-- 영향 행이 0이 될 때까지 반복

扩展 → 迁移 → 收缩(Expand / Migrate / Contract)

这是可回滚 schema 变更的标准模式。核心规则是:一次部署不要同时包含两个阶段。

[1차 배포 — 확장]
  새 컬럼 추가 (NULL 허용, 기본값 있음)
  애플리케이션: 새 컬럼과 옛 컬럼에 모두 쓰고, 읽기는 옛 컬럼
  → 이 시점에서 롤백하면? 새 컬럼을 아무도 안 읽으니 안전

[2차 — 이관]
  기존 데이터 백필 (배치로 쪼개서)
  검증: 두 컬럼 값이 일치하는가

[3차 배포 — 전환]
  애플리케이션: 읽기를 새 컬럼으로
  → 롤백하면 옛 컬럼을 읽는데, 계속 써 왔으니 값이 있다. 안전

[4차 배포 — 축소]
  애플리케이션: 옛 컬럼 쓰기 중단
  충분한 관찰 기간 후 옛 컬럼 DROP
  → 이 시점에서야 비가역이 된다

看起来很慢,但这一模式的全部价值就是每个阶段都能回滚。一次性改列名只需 30 秒,失败时服务会停止;这个模式可能持续两周,却不会在任何时点停服。

变更管理台账

SI 项目中的 DDL 不能由开发人员随意执行。应维护变更管理台账,生产变更必须经过批准。

项目 为什么需要
变更 ID/日期 追踪单位
目标对象 确定影响范围的起点
DDL 脚本文件 实际执行内容
回滚脚本文件 没有就不批准
预计耗时 估算服务中断时间
受影响系统 需要通知的集成方
批准人 责任

台账的核心价值是强制要求回滚脚本。编写回滚脚本时,会在部署前发现“这项变更无法回滚”,从而推动改用扩展—迁移—收缩设计。

schema 变更与应用部署顺序

这里也经常出错。

一句话:增加时数据库先行,删除时应用先行。

滚动部署期间,旧版与新版会同时运行。因此 schema 必须始终保持两种版本都能工作,这正是扩展—迁移—收缩的根本理由。

蓝绿部署不能解决 schema 问题

讨论零停机部署时,必须指出一点。

计算资源很容易复制,数据库通常仍然共享。 蓝绿部署可以让应用回滚缩短到秒级, 却完全没有解决 schema 问题。

如果蓝、绿环境访问同一个数据库,schema 就必须同时兼容两版应用,最终仍回到同一个结论。

迁移验证

应用变更后要分四层验证。

1. 스키마   컬럼·타입·제약·인덱스가 의도대로인가
2. 데이터   행 수 · 합계 · NULL 개수 · 체크섬이 보존됐는가
3. 성능     주요 쿼리의 실행계획과 응답시간이 나빠지지 않았는가
4. 앱       핵심 기능 스모크 테스트

第 2 项中的校验和尤其有用。只比较行数与总和,无法发现“少了一条,另一条翻倍”的情况。

-- 이관 전 기준선을 만들어 둔다
CREATE TABLE MIG_BASELINE AS
SELECT COUNT(*) AS CNT, SUM(ORD_AMT) AS AMT,
       COUNT(DISTINCT CUST_ID) AS CUSTS FROM ORDERS;
-- 이관 후 같은 쿼리로 비교

诀窍是作业前先建立基线。作业后才问“原来有多少条”,已经太晚。

在实际项目中

schema 变更演变成事故,通常有两条路径。

第一是。添加 NOT NULL 会扫描全部行并锁表,期间所有使用该表的请求都要等待。在开发数据库中只需 0.2 秒,在一千万行的生产表上可能需要数分钟,这几分钟与服务停机没有区别。

第二是顺序。先部署应用,它会读取尚不存在的列而崩溃;先修改 schema,旧应用又可能违反新约束。因此,“同时部署两者”不是计划;真正的计划,是让无论哪一方先执行都能承受。

也有人试图用蓝绿部署解决,但两种颜色访问同一个数据库这一事实没有改变。应用可以无中断切换,schema 仍然只有一份。