Files
DjangoAdmin_FastAPI_EleVue/document/数据库迁移.txt
T
2026-09-12 17:39:13 +08:00

326 lines
19 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
================================================================================
数据库迁移指南(scripts/migrate_db.py)
MySQL → PostgreSQL / SQL Server / SQLite / Oracle
================================================================================
一、脚本说明
================================================================================
migrate_db.py 是项目内置的跨库迁移脚本,把「源库」整体搬到「目标库」:
- 目标库结构用模型 metadata 生成(Base.metadata.create_all,方言可移植,模型即 head,
含 0002 的 city.parent_code nullable 等已落地模型定义)
- 数据按拓扑序逐表 SELECT ... ORDER BY id,显式带 id 批量插入,保住无外键裸列引用
(user.dept_id、role_menu.role_id/menu_id、menu.parent_id 等)
- 迁移后按方言重置自增:
PostgreSQL SERIAL → setval(seq, MAX(id))(空表 START 1)
SQL Server IDENTITY → IDENTITY_INSERT 显式插 id + DBCC CHECKIDENT RESEED
Oracle 补 sequence(START WITH MAX(id)+1)+ BEFORE INSERT 触发器
(create_all 只生成 INTEGER NOT NULL,无原生自增)
MySQL/SQLite 显式插 id 后 auto_increment/rowid 自动跟随,无需处理
- 全部耗时步骤都带「缓冲提示」([·] ...请勿中断 → [✓] ...完成(耗时)),不会显得卡死
特点:源/目标连接全部由 CLI 显式参数给出,不依赖 .env,也无需先切 DB_DRIVER。
二、前置准备
================================================================================
1. 源库(MySQL)可正常访问。
2. 目标库所需驱动已包含在 requirements.txt,执行一次即可:
pip install -r requirements.txt
各目标库对应驱动:
PostgreSQL psycopg[binary](psycopg3)
SQL Server pymssql
SQLite 内置(无需额外驱动)
Oracle oracledb(thin 模式,无需安装 Oracle Client)
3. 可用 --dry-run 先干跑核对计划(只连源、逐表数行数、打印计划,不碰目标库)。
三、驱动与默认端口(含别名)
================================================================================
目标库 --dst-driver 默认端口 --dst-db 填什么
---------------------------------------------------------------------
MySQL mysql 3306 数据库名
PostgreSQL postgresql 5432 数据库名(含点号会自动双引号)
SQL Server mssql 1433 数据库名
SQLite sqlite 无端口 SQLite 文件路径(相对/绝对皆可)
Oracle oracle 1521 SERVICE_NAME(如 ORCLPDB1、XE,非库名)
驱动别名:postgres、pg → postgresql;sqlserver、sql_server → mssql;sqlite3 → sqlite
四、迁移命令与输出案例
================================================================================
以下源库均假设:
MySQL 127.0.0.1:3306,库名 djangoadmin.fastapi.elevue,root / root
本项目共 23 张表(前缀 fastapi_),全量迁移约 4843 行(其中 fastapi_city 4150 行)。
4.1 MySQL → PostgreSQL
--------------------------------------------------------------------------------
命令:
python scripts/migrate_db.py \
--src-driver=mysql --src-host=127.0.0.1 --src-port=3306 \
--src-db=djangoadmin.fastapi.elevue --src-user=root --src-pass=root \
--dst-driver=postgresql --dst-host=localhost --dst-port=5432 \
--dst-db=djangoadmin.fastapi.elevue --dst-user=postgres --dst-pass=你的密码 \
--drop-target-first
输出案例:
==============================================================
[迁移开始] 源: mysql:djangoadmin.fastapi.elevue → 目标: postgresql:djangoadmin.fastapi.elevue
[迁移开始] 待迁移 23 张表
==============================================================
[·] [1/5] 建目标库...(可能耗时较长,请勿中断)
· 正在连接 postgres 维护库...
· 正在删除旧目标库 "djangoadmin.fastapi.elevue"(WITH FORCE)...
· 正在创建目标库 "djangoadmin.fastapi.elevue"(UTF8)...
[✓] [1/5] 建目标库 完成(耗时 947ms)
[·] [2/5] 建表结构(23 张表 create_all)...(可能耗时较长,请勿中断)
[✓] [2/5] 建表结构(23 张表 create_all) 完成(耗时 1.1s),23 张表
[·] [3/5] 逐表迁移数据...(可能耗时较长,请勿中断)
· [1/23] 正在迁移 fastapi_dept...
[✓ fastapi_dept] 共 23 行(耗时 12ms)
· [2/23] 正在迁移 fastapi_menu...
[✓ fastapi_menu] 共 54 行(耗时 110ms)
· [3/23] 正在迁移 fastapi_city...
[fastapi_city] +1000(累计 1000)
[fastapi_city] +1000(累计 2000)
[fastapi_city] +1000(累计 3000)
[fastapi_city] +1000(累计 4000)
[fastapi_city] +150(累计 4150)
[✓ fastapi_city] 共 4150 行(耗时 850ms)
· [4/23] 正在迁移 fastapi_user...
...
· [23/23] 正在迁移 fastapi_login_log...
[✓ fastapi_login_log] 共 5 行(耗时 13ms)
[✓] [3/5] 逐表迁移数据 完成(耗时 3.5s),共 4843 行
[·] [4/5] 重置自增序列...(可能耗时较长,请勿中断)
[✓] [4/5] 重置自增序列 完成(耗时 412ms) ← SERIAL setval 到 MAX(id)
[·] [5/5] 逐表行数校验...(可能耗时较长,请勿中断)
[fastapi_dept] source=23 target=23 PASS
...
[fastapi_city] source=4150 target=4150 PASS
...
[✓] [5/5] 逐表行数校验 完成(耗时 103ms),23 张表全部 PASS
==============================================================
[迁移完成] 成功:4843 行 / 23 张表 / 总耗时 6.0s
==============================================================
4.2 MySQL → SQL Server(mssql)
--------------------------------------------------------------------------------
命令:
python scripts/migrate_db.py \
--src-driver=mysql --src-host=127.0.0.1 --src-port=3306 \
--src-db=djangoadmin.fastapi.elevue --src-user=root --src-pass=root \
--dst-driver=mssql --dst-host=localhost --dst-port=1433 \
--dst-db=djangoadmin.fastapi.elevue --dst-user=sa --dst-pass=你的密码 \
--drop-target-first
说明:mssql 迁移时自动 SET IDENTITY_INSERT ON 保留原 id,迁移后 DBCC CHECKIDENT RESEED。
输出案例(真实运行):
==============================================================
[迁移开始] 源: mysql:djangoadmin.fastapi.elevue → 目标: mssql:djangoadmin.fastapi.elevue
[迁移开始] 待迁移 23 张表
==============================================================
[·] [1/5] 建目标库...(可能耗时较长,请勿中断)
· 正在连接 SQL Server(master 维护库)...
· 正在删除旧目标库 [djangoadmin.fastapi.elevue](SINGLE_USER + DROP)...
· 正在创建目标库 [djangoadmin.fastapi.elevue]...
[✓] [1/5] 建目标库 完成(耗时 304ms)
[·] [2/5] 建表结构(23 张表 create_all)...(可能耗时较长,请勿中断)
[✓] [2/5] 建表结构(23 张表 create_all) 完成(耗时 743ms),23 张表
[·] [3/5] 逐表迁移数据...(可能耗时较长,请勿中断)
· [1/23] 正在迁移 fastapi_dept...
[fastapi_dept] +23(累计 23)
[✓ fastapi_dept] 共 23 行(耗时 25ms)
· [2/23] 正在迁移 fastapi_menu...
[fastapi_menu] +54(累计 54)
[✓ fastapi_menu] 共 54 行(耗时 102ms)
· [3/23] 正在迁移 fastapi_role...
...
· [6/23] 正在迁移 fastapi_city...
[fastapi_city] +1000(累计 1000)
...
[fastapi_city] +150(累计 4150)
[✓ fastapi_city] 共 4150 行(耗时 3.9s)
· [7/23] 正在迁移 fastapi_category...
...
· [23/23] 正在迁移 fastapi_login_log...
[✓ fastapi_login_log] 共 5 行(耗时 13ms)
[✓] [3/5] 逐表迁移数据 完成(耗时 5.3s),共 4843 行
[·] [4/5] 重置自增序列...(可能耗时较长,请勿中断)
[✓] [4/5] 重置自增序列 完成(耗时 90ms)
[·] [5/5] 逐表行数校验...(可能耗时较长,请勿中断)
[fastapi_dept] source=23 target=23 PASS
...
[fastapi_city] source=4150 target=4150 PASS
...
[✓] [5/5] 逐表行数校验 完成(耗时 100ms),23 张表全部 PASS
==============================================================
[迁移完成] 成功:4843 行 / 23 张表 / 总耗时 6.6s
==============================================================
4.3 MySQL → SQLite
--------------------------------------------------------------------------------
命令:
python scripts/migrate_db.py \
--src-driver=mysql --src-host=127.0.0.1 --src-port=3306 \
--src-db=djangoadmin.fastapi.elevue --src-user=root --src-pass=root \
--dst-driver=sqlite --dst-db=D:/data/djangoadmin.fastapi.elevue.db \
--drop-target-first
说明:--dst-db 即数据库文件完整路径(相对/绝对皆可);SQLite 无建库概念,
--drop-target-first 时删除旧文件后重建;自增 id 由 rowid 自动跟随,无需重置。
输出案例:
==============================================================
[迁移开始] 源: mysql:djangoadmin.fastapi.elevue → 目标: sqlite:D:/data/djangoadmin.fastapi.elevue.db
[迁移开始] 待迁移 23 张表
==============================================================
[·] [1/5] 建目标库...(可能耗时较长,请勿中断)
· 检查/准备 sqlite 文件 D:\data\djangoadmin.fastapi.elevue.db...
· 删除旧 sqlite 文件 D:\data\djangoadmin.fastapi.elevue.db...
[✓] [1/5] 建目标库 完成(耗时 8ms)
[·] [2/5] 建表结构(23 张表 create_all)...(可能耗时较长,请勿中断)
[✓] [2/5] 建表结构(23 张表 create_all) 完成(耗时 96ms),23 张表
[·] [3/5] 逐表迁移数据...(可能耗时较长,请勿中断)
· [1/23] 正在迁移 fastapi_dept...
[✓ fastapi_dept] 共 23 行(耗时 4ms)
...
· [6/23] 正在迁移 fastapi_city...
[✓ fastapi_city] 共 4150 行(耗时 850ms)
...
[✓] [3/5] 逐表迁移数据 完成(耗时 1.2s),共 4843 行
[·] [4/5] 重置自增序列...(可能耗时较长,请勿中断)
· MySQL/SQLite 自动跟随,无需处理
[✓] [4/5] 重置自增序列 完成(耗时 0ms)(无需处理)
[·] [5/5] 逐表行数校验...(可能耗时较长,请勿中断)
[✓] [5/5] 逐表行数校验 完成(耗时 100ms),23 张表全部 PASS
==============================================================
[迁移完成] 成功:4843 行 / 23 张表 / 总耗时 1.4s
==============================================================
4.4 MySQL → Oracle
--------------------------------------------------------------------------------
命令:
python scripts/migrate_db.py \
--src-driver=mysql --src-host=127.0.0.1 --src-port=3306 \
--src-db=djangoadmin.fastapi.elevue --src-user=root --src-pass=root \
--dst-driver=oracle --dst-host=localhost --dst-port=1521 \
--dst-db=ORCLPDB1 --dst-user=fastapi --dst-pass=你的密码 \
--drop-target-first
说明:
- Oracle 无"数据库名",--dst-db 填 SERVICE_NAME(如 ORCLPDB1、XE);
schema 即 --dst-user 连接的用户(无需也无法"建库",连上即视为就绪),
该用户需有 CONNECT, RESOURCE 权限
- Oracle create_all 对 Integer 主键只生成 INTEGER NOT NULL(无 identity),
迁移后脚本会自动补 sequence(START WITH MAX(id)+1)+ BEFORE INSERT 触发器,
保证后续 save() 能取到新 id
- --drop-target-first 时尽力 DROP TABLE ... PURGE 清理旧项目表
输出案例:
==============================================================
[迁移开始] 源: mysql:djangoadmin.fastapi.elevue → 目标: oracle:ORCLPDB1
[迁移开始] 待迁移 23 张表
==============================================================
[·] [1/5] 建目标库...(可能耗时较长,请勿中断)
· 正在连接 Oracle 服务 ORCLPDB1(schema=fastapi)...
· drop-first:清理 23 张旧项目表(DROP TABLE ... PURGE)...
[✓] [1/5] 建目标库 完成(耗时 4.6s)
[·] [2/5] 建表结构(23 张表 create_all)...(可能耗时较长,请勿中断)
[✓] [2/5] 建表结构(23 张表 create_all) 完成(耗时 2.4s),23 张表
[·] [3/5] 逐表迁移数据...(可能耗时较长,请勿中断)
· [1/23] 正在迁移 fastapi_dept...
[✓ fastapi_dept] 共 23 行(耗时 15ms)
...
· [6/23] 正在迁移 fastapi_city...
[✓ fastapi_city] 共 4150 行(耗时 2m12s)
...
[✓] [3/5] 逐表迁移数据 完成(耗时 2m41s),共 4843 行
[·] [4/5] 重置自增序列...(可能耗时较长,请勿中断)
[✓] [4/5] 重置自增序列 完成(耗时 6.2s) ← 补 sequence + BEFORE INSERT 触发器
[·] [5/5] 逐表行数校验...(可能耗时较长,请勿中断)
[✓] [5/5] 逐表行数校验 完成(耗时 2.6s),23 张表全部 PASS
==============================================================
[迁移完成] 成功:4843 行 / 23 张表 / 总耗时 2m54s
==============================================================
五、常用参数详解
================================================================================
--drop-target-first 先删目标库再重建(Oracle 清旧表、SQLite 删旧文件)。默认关;
重跑迁移时建议开启,保证幂等。
--dry-run 只连源库、逐表数行数、打印计划,不触碰目标库。
--chunk-size N 每批插入行数,默认 1000;大表可调大(如 5000)提升吞吐。
--only-tables a,b 只迁移指定表(全名或去前缀短名,如 --only-tables user,role),续传用。
--skip-existing 目标表行数已等于源表则跳过(配合续传/增量)。
--skip-verify 跳过迁移后的逐表行数校验。
--single-transaction 全部表包进一个大事务(默认每表一个事务;大表建议保持默认,
避免单事务过大)。
口令脱敏 --src-pass/--dst-pass 留空时回落环境变量 SRC_DB_PASS/DST_DB_PASS,
避免口令进 shell 历史。
六、常见问题与注意事项
================================================================================
1. 目标库已存在且未加 --drop-target-first:脚本只做 create_all(IF NOT EXISTS 语义)+ 补数据,
已存在表的数据会与迁移数据叠加,可能主键冲突。重跑请加 --drop-target-first。
2. 迁移后登录报 "no such table" / 表不存在:确认 --dst-db 指向正确(Oracle 填 SERVICE_NAME,
不是库名;SQLite 填文件路径)。
3. 中文乱码:源 MySQL 连接串已带 charset=utf8mb4;PG 建库强制 UTF8/TEMPLATE template0;
mssql 连接串 charset=utf8。若目标已建成非 UTF8 编码库,删除重建(--drop-target-first)。
4. Oracle 迁移后新增记录 id 异常:migrate_db.py 已自动补 sequence+触发器,
若手动建过同名 sequence 会先删后建(user_sequences 里按名查)。
5. 自增 id 未延续原值:PG 用 setval、MSSQL 用 DBCC CHECKIDENT、Oracle 用 sequence 均已自动处理;
如仍异常可手动:
PostgreSQL:SELECT setval(pg_get_serial_sequence('fastapi_user','id'), (SELECT max(id) FROM fastapi_user));
SQL Server:DBCC CHECKIDENT ('fastapi_user', RESEED, <max_id>)
Oracle: DROP SEQUENCE fastapi_user_id_seq; CREATE SEQUENCE fastapi_user_id_seq START WITH <max_id+1>;
6. 迁移过程被中断:已迁移的表按每表一个事务提交,未迁完的表重跑即可;配合 --only-tables / --skip-existing
断点续传。
================================================================================
文档版本:1.0 | 更新日期:2026-08-14 | 适用范围:scripts/migrate_db.py
================================================================================
其他:整体流程
┌─────────────────────────────────────────────────────┐
│ 1. 解析 CLI 参数 │
│ --src-* / --dst-* / --drop-target-first │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 2. create_database_if_missing() │
│ ├─ MySQL: CREATE DATABASE IF NOT EXISTS │
│ ├─ PG: CREATE DATABASE (连接 postgres) │
│ ├─ MSSQL: CREATE DATABASE (连接 master) │
│ ├─ Oracle: 不建库,只清理表 │
│ └─ SQLite: mkdir + 可选删文件 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 3. Base.metadata.create_all(dst_engine) │
│ ├─ 方言适配:自动生成对应数据库的 DDL │
│ ├─ 按 ordered_tables() 顺序建表 │
│ └─ 包含所有模型定义(字段/索引/约束) │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 4. 后续:逐表泵数据 → 重置序列 → 校验 │
└─────────────────────────────────────────────────────┘