feat: SQLite → MySQL 数据迁移脚本 + 建库建账号 SQL(P3)

- scripts/migrate_sqlite_to_mysql.py(新增):源库完整性校验 → 列宽审计(SQLite 不强制
  长度,MySQL 严格模式下超长直接报错,先拦下)→ 建目标 schema(复用平台的 create_all
  + _sync_columns + 唯一索引)→ 逐表单事务搬行 → 逐表行数 + 全行 SHA-256 比对 →
  写库环境标签与 schema_version。支持 --dry-run / --mode verify / --schema-only;
  --env prod 必须 --allow-prod(+ 交互确认或 --yes);目标库已登记别的环境时拒绝
- scripts/sql/init_mysql_5.7.sql(新增):建 auto_control / auto_control_dev 两个库
  (utf8mb4 + utf8mb4_bin)+ 专用账号与授权
- doc/DEPLOY.md §7.2:补 SQLite→MySQL 迁移流程与注意事项

比对口径踩坑记录:验证哈希必须两边都从**驱动层原生读**。走 ORM/Core 的 typed
select 会把 Boolean 读成 True/False,而源库 SQLite 原始读出来是 1/0 —— 三张带
布尔列的表(user/device/task_job)会全部误报"不一致"。现在统一用原生 SQL 回读 +
值规范化(bool→int、整数值浮点→int、bytes→str)。

自测(目标用 SQLite 代跑,MySQL 专属部分待 P4 真库验证):12 张表逐表 SHA-256 一致、
重复迁移幂等、verify 模式通过。
This commit is contained in:
2026-09-13 10:37:15 +08:00
parent 2a3775ffd5
commit 429aaf6283
3 changed files with 434 additions and 2 deletions
+25 -2
View File
@@ -268,9 +268,32 @@ docker restart python-app # 容器场景
### 7.2 数据库迁移
- 表结构变化由 `init_db()` 自动处理:`create_all()` 建新表 + `SCHEMA_MIGRATIONS` 补列(幂等,失败不阻塞启动)
- **表结构变化**由 `init_db()` 自动处理:`create_all()` 建缺表 + `_sync_columns()` 补缺列(幂等,失败不阻塞启动,见 [DATA_MODEL.md](DATA_MODEL.md) §4)
- 旧 `groups.json` / `jobs.json` 首次启动自动迁移并归档为 `.migrated`
- **跨版本恢复**:用「导入恢复」上传旧库 → 预览会提示 schema 版本差异 → 应用后重启(新版本会自动补迁移)
- **跨版本恢复**:用「导入恢复」上传旧库 → 预览会提示 schema 版本差异 → 应用后重启(新版本会自动补列/补空表)
#### SQLite → MySQL(一次性)
首次把平台从单文件 SQLite 换到 MySQL 时,用迁移脚本搬数据:
```bash
# 0) 先在 .env 里配好 DB_HOST/DB_USER/DB_PASSWORD/DB_NAME(或用 --target-url)
# 1) 干跑:审计源库 + 建目标 schema,不搬数据
python scripts/migrate_sqlite_to_mysql.py --sqlite data/users.db --env dev --dry-run
# 2) 正式搬(目标库已有的数据会被整表替换;单事务,失败自动回滚)
python scripts/migrate_sqlite_to_mysql.py --sqlite data/users.db --env dev
# 3) 复验(只比对不写)
python scripts/migrate_sqlite_to_mysql.py --sqlite data/users.db --env dev --mode verify
```
脚本做的事:源库完整性校验 → **列宽审计**(SQLite 不强制长度,MySQL 严格模式下超长会直接报错,
所以先拦下来)→ 建目标 schema(复用平台的 `create_all` + 补列 + 唯一索引)→ 逐表搬行
(单事务)→ **逐表行数 + 全行 SHA-256 比对** → 写库环境标签与 `schema_version`。
> `--env prod` 必须额外加 `--allow-prod`,交互终端还要手打 `prod` 确认(这是唯一会往生产库灌数据的入口)。
> 目标库若已登记为别的环境(`app_meta.deployment_env`),除非 `--force-env` 否则拒绝。
建库建账号见 `scripts/sql/init_mysql_5.7.sql`(utf8mb4 + `utf8mb4_bin`,两个库对应 dev/prod)。
### 7.3 跨机迁移(换部署机)