diff --git a/docs/db-migration-runbook.md b/docs/db-migration-runbook.md index 5b99d4f9..74ed2e46 100644 --- a/docs/db-migration-runbook.md +++ b/docs/db-migration-runbook.md @@ -30,6 +30,27 @@ ## 3. 迁移步用法(`backend/scripts/db_migrate.py`) +### 3.0 强制前置步 —— 任何 `--apply` **之前**:迁移前像 dump 哈希 + +> 来源:`CMA-KPI-20261003-D-T1` 只读回读复核(2026-10-05)。落地前两侧(设计 10:11 / 执行 10:38)**只留了行数/计数基线**, +> 未留全表 `(PK, 受影响列)` 有序 dump 哈希 ⇒ **迁移前像不可得**,事后只能用「四时点基线谓词对拍 + 值域指纹 + 时间戳 + +> `MODIFY` 类型保持」四路**替代取证**。本步是事后自证「**存量取值零改写**」的**唯一直接依据**,不可省、不可推迟到 `--apply` 之后。 + +对本任务**每一张受影响表**,在 `--apply` **之前**落一次全表有序 `(PK, 受影响列)` dump,记录 `sha256 + 行数 + 字节数`; +产物归档 `backups/pre-ddl-/`(与 `backups/pre-ddl-entity-dim-20260923/` 同约定),随任务件留痕。 + +可直接复制(把 `DB / TBL / COLS` 换成任务值;`COLS` **首位必须是主键**): + +```bash +DB=cma TBL=treasury_limits COLS='id,status,limit_amount' D="backups/pre-ddl-$(date +%Y%m%d-%H%M%S)" sh -c 'mkdir -p "$D"; mysql -N -B --default-character-set=utf8mb4 "$DB" -e "SELECT $COLS FROM $TBL ORDER BY ${COLS%%,*}" > "$D/$TBL.before.tsv"; sha256sum "$D/$TBL.before.tsv"; wc -lc < "$D/$TBL.before.tsv"' +``` + +- `ORDER BY ${COLS%%,*}`(即主键)保证字节级确定性:同一份数据两跑,`sha256` 必须一致。 +- 行数与 `SELECT COUNT(*)` 对账:`wc -l` 即行数(`-N` 无表头;末行缺换行符时 ±1 属正常)。 +- 多表:每张受影响表各跑一次,逐表记录「表名 + sha256 + 行数 + 字节数」。 +- 现成实现可复用 ops 探针 `wecom-ops/scripts/probe_cma_kpi_t1_readback_20261005.py --check`(自动落 `02b-current-id-tv.json` 全量行值基线)。 +- 本步只用**只读**语句(`SELECT` + `sha256sum`),零生产写;`--apply` 与回滚命令见下。 + ```bash cd /root/cma-management/backend ./venv/bin/python3 scripts/db_migrate.py --check # 只读预检(默认动作) @@ -53,7 +74,8 @@ cd /root/cma-management/backend 2. 本地 `--check` 应能列出该表缺失(证明 ORM 已注册)。 3. 提交推送 → 部署链 `[4b]` 用特权账号建表 → 重启 → 冒烟 `schema_check` 通过。 4. 若模型**改列**(新增/改名/改类型):`create_all` 不处理 → 必须写独立迁移脚本/`ALTER`(参考 - `backend/scripts/migrate_ext_input_ledgers_20260915.py`、`treasury_migrate.py` 的 `up()/down()` 模式)。 + `backend/scripts/migrate_ext_input_ledgers_20260915.py`、`treasury_migrate.py` 的 `up()/down()` 模式); + **执行任何 `ALTER` 前先走 §3.0 迁移前像(全表有序 dump 哈希)**。 5. **不要**让运行库账号重新获得 CREATE 来「绕过」:那正是事故根因。 ## 5. `cma_api` 的 CREATE 授权回收(项目Bot 执行)