Files
2026-09-11 11:39:07 +08:00

280 lines
9.5 KiB
Markdown
Raw Permalink 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.
# 数据库迁移工具使用说明
## 概述
`migrate_db.py` 是一个跨数据库迁移工具,支持将数据从 MySQL 迁移到 PostgreSQL、SQL Server、SQLite、Oracle 等数据库。
## 核心设计
**参考FastAPI版本的设计思路:**
- 使用 SQLAlchemy ORM 模型定义表结构(方言可移植)
- 数据按拓扑序逐表迁移,显式带 id 批量插入
- 迁移后按方言重置自增序列
## 特性
- ✅ 支持 MySQL → PostgreSQL / SQL Server / SQLite / Oracle
- ✅ 自动从 Django 模型加载表结构定义
- ✅ 按拓扑序迁移表,保证数据完整性
- ✅ 批量插入,高性能迁移
- ✅ 自动重置自增序列(PG/MSSQL/Oracle)
- ✅ 支持干跑模式(dry-run)
- ✅ 支持断点续传(--only-tables)
- ✅ 迁移后自动校验数据行数
## 依赖安装
根据目标数据库安装对应的 Python 驱动:
```bash
# PostgreSQL
pip install psycopg
# SQL Server
pip install pymssql
# Oracle
pip install oracledb
# MySQL(通常已安装)
pip install pymysql
```
## 使用方法
### 1. 显式指定源库和目标库
```bash
# MySQL → PostgreSQL
python scripts/migrate_db.py \
--src-driver=mysql --src-host=127.0.0.1 --src-port=3306 \
--src-db=djangoadmin.django.elevue --src-user=root --src-pass=root \
--dst-driver=postgresql --dst-host=127.0.0.1 --dst-port=5432 \
--dst-db=djangoadmin.django.elevue --dst-user=postgres --dst-pass=postgres
# MySQL → SQL Server
python scripts/migrate_db.py \
--src-driver=mysql --src-host=127.0.0.1 --src-port=3306 \
--src-db=djangoadmin.django.elevue --src-user=root --src-pass=root \
--dst-driver=mssql --dst-host=127.0.0.1 --dst-port=1433 \
--dst-db=djangoadmin --dst-user=sa --dst-pass=your_password
# MySQL → SQLite
python scripts/migrate_db.py \
--src-driver=mysql --src-host=127.0.0.1 --src-port=3306 \
--src-db=djangoadmin.django.elevue --src-user=root --src-pass=root \
--dst-driver=sqlite --dst-db=./data/djangoadmin.db
# MySQL → Oracle
python scripts/migrate_db.py \
--src-driver=mysql --src-host=127.0.0.1 --src-port=3306 \
--src-db=djangoadmin.django.elevue --src-user=root --src-pass=root \
--dst-driver=oracle --dst-host=127.0.0.1 --dst-port=1521 \
--dst-db=ORCL --dst-user=system --dst-pass=oracle
```
### 2. 使用 .env 配置(如果可用)
```bash
# 只需指定目标库,源库从 .env 读取
python scripts/migrate_db.py \
--dst-driver=postgresql --dst-host=127.0.0.1 --dst-port=5432 \
--dst-db=djangoadmin --dst-user=postgres --dst-pass=postgres
```
### 3. 干跑模式
先查看迁移计划,不实际执行:
```bash
python scripts/migrate_db.py --dry-run \
--src-driver=mysql --src-host=127.0.0.1 --src-port=3306 \
--src-db=djangoadmin.django.elevue --src-user=root --src-pass=root \
--dst-driver=postgresql --dst-db=djangoadmin \
--dst-user=postgres --dst-pass=postgres
```
输出示例:
```
[==============================================================]
[DRY-RUN 开始] 计划迁移 27 张表 → postgresql:djangoadmin
[==============================================================]
- django_dept: 31 行
- django_menu: 217 行
- django_role: 21 行
...
[DRY-RUN 完成] 27 张表核对完毕(耗时 1.2s),未触碰目标库
```
### 4. 断点续传
如果迁移中断,可以使用 `--only-tables` 继续迁移指定的表:
```bash
python scripts/migrate_db.py \
--only-tables=django_user,django_role,django_menu \
--src-driver=mysql --src-host=127.0.0.1 --src-port=3306 \
--src-db=djangoadmin.django.elevue --src-user=root --src-pass=root \
--dst-driver=postgresql --dst-host=127.0.0.1 --dst-port=5432 \
--dst-db=djangoadmin.django.elevue --dst-user=postgres --dst-pass=postgres
```
### 5. 跳过已存在的表
```bash
python scripts/migrate_db.py --skip-existing \
--src-driver=mysql --src-host=127.0.0.1 --src-port=3306 \
--src-db=djangoadmin.django.elevue --src-user=root --src-pass=root \
--dst-driver=postgresql --dst-host=127.0.0.1 --dst-port=5432 \
--dst-db=djangoadmin.django.elevue --dst-user=postgres --dst-pass=postgres
```
## 参数说明
| 参数 | 说明 | 默认值 |
|------|------|--------|
| `--src-driver` | 源库驱动 | mysql |
| `--src-host` | 源库主机 | 127.0.0.1 |
| `--src-port` | 源库端口 | 自动识别 |
| `--src-db` | 源库名称 | 必填 |
| `--src-user` | 源库用户名 | root |
| `--src-pass` | 源库密码 | 空 |
| `--dst-driver` | 目标库驱动 | 必填 |
| `--dst-host` | 目标库主机 | 127.0.0.1 |
| `--dst-port` | 目标库端口 | 自动识别 |
| `--dst-db` | 目标库名称 | 必填 |
| `--dst-user` | 目标库用户名 | postgres |
| `--dst-pass` | 目标库密码 | 空 |
| `--drop-target-first` | 先删除目标库 | False |
| `--dry-run` | 干跑模式 | False |
| `--chunk-size` | 每批插入行数 | 1000 |
| `--only-tables` | 只迁移指定表 | 空(全部) |
| `--skip-existing` | 跳过已存在的表 | False |
| `--skip-verify` | 跳过校验 | False |
| `--single-transaction` | 单事务模式 | False |
| `--prefix` | 表前缀 | 从 .env 读取 |
## 迁移流程
1. **加载模型** - 从 Django 模型加载表结构定义
2. **建目标库** - 自动创建目标数据库(如不存在)
3. **建表结构** - 使用 SQLAlchemy ORM 模型创建表(方言可移植)
4. **迁移数据** - 按拓扑序逐表批量插入数据
5. **重置序列** - 针对 PG/MSSQL/Oracle 重置自增序列
6. **校验数据** - 对比源库和目标库的行数
## 注意事项
1. **表前缀**:默认使用 `.env` 中的 `DATABASE_PREFIX`(默认为 `django_`)
2. **字符集**:MySQL 使用 utf8mb4,PostgreSQL 使用 UTF8
3. **自增序列**:迁移后会自动重置,确保新数据能正确生成 ID
4. **外键关系**:项目使用裸列引用(无外键约束),按拓扑序迁移可保证引用完整性
5. **软删除**:所有表都包含 `is_delete` 字段,迁移时会保留该字段
## 常见问题
### Q: 迁移过程中断怎么办?
使用 `--only-tables` 参数指定需要继续迁移的表:
```bash
python scripts/migrate_db.py --only-tables=django_user,django_role
```
### Q: 如何查看迁移进度?
迁移过程中会显示每个表的迁移进度:
```
[django_user] +100(累计 100)
[django_user] +100(累计 200)
[✓ django_user] 共 500 行(耗时 1.2s)
```
### Q: 目标库已存在数据怎么办?
使用 `--drop-target-first` 参数先删除目标库:
```bash
python scripts/migrate_db.py --drop-target-first
```
或者使用 `--skip-existing` 跳过已存在的表:
```bash
python scripts/migrate_db.py --skip-existing
```
### Q: 如何只迁移部分表?
使用 `--only-tables` 参数指定表名(逗号分隔):
```bash
python scripts/migrate_db.py --only-tables=django_user,django_role,django_menu
```
## 示例场景
### 场景1:开发环境 MySQL → 测试环境 PostgreSQL
```bash
python scripts/migrate_db.py \
--src-driver=mysql --src-host=127.0.0.1 --src-port=3306 \
--src-db=djangoadmin.django.elevue --src-user=root --src-pass=root \
--dst-driver=postgresql --dst-host=test-db-server --dst-port=5432 \
--dst-db=djangoadmin_test --dst-user=postgres --dst-pass=test_password \
--drop-target-first
```
### 场景2:生产环境迁移(带校验)
```bash
python scripts/migrate_db.py \
--src-driver=mysql --src-host=prod-db-server --src-port=3306 \
--src-db=djangoadmin_prod --src-user=readonly --src-pass=readonly_password \
--dst-driver=postgresql --dst-host=new-db-server --dst-port=5432 \
--dst-db=djangoadmin_prod --dst-user=postgres --dst-pass=new_password \
--chunk-size=5000
```
### 场景3:迁移到本地 SQLite(开发调试)
```bash
python scripts/migrate_db.py \
--src-driver=mysql --src-host=127.0.0.1 --src-port=3306 \
--src-db=djangoadmin.django.elevue --src-user=root --src-pass=root \
--dst-driver=sqlite --dst-db=./data/debug.db \
--skip-verify
```
## 技术架构
### 核心设计(参考FastAPI版本)
1. **模型定义**:使用 SQLAlchemy ORM 定义表结构,保证方言可移植
2. **拓扑排序**:基础表在前,依赖表在后,保证引用完整性
3. **批量插入**:显式带 id 插入,保住裸列引用关系
4. **序列重置**:按方言重置自增序列(PG setval / MSSQL DBCC CHECKIDENT / Oracle sequence+trigger)
### 与 FastAPI 版本的差异
| 特性 | FastAPI版本 | Django版本 |
|------|------------|------------|
| 模型来源 | SQLAlchemy ORM 模型 | Django 模型动态转换 |
| 配置来源 | core.config | config.env |
| 表前缀 | 从配置读取 | 从Django配置读取 |
| 模型加载 | 直接导入 | 动态创建 |
## 成功案例
```
[迁移开始] 源: mysql:djangoadmin.django.elevue → 目标: postgresql:djangoadmin.django.elevue
[迁移开始] 待迁移 35 张表
[✓] [1/5] 建目标库 完成(耗时 62ms)
[✓] [2/5] 建表结构(35 张表 create_all) 完成(耗时 74ms)
[✓] [3/5] 逐表迁移数据 完成(耗时 37.5s),共 784612 行
[✓] [4/5] 重置自增序列 完成(耗时 2.2s)
[✓] [5/5] 逐表行数校验 完成(耗时 605ms),35 张表全部 PASS
[迁移完成] 成功:784612 行 / 35 张表 / 总耗时 40.8s
```
**迁移的35张表包括:**
- Django内置表(11张):auth_permission, auth_group, auth_user, auth_group_permissions, auth_user_groups, auth_user_user_permissions, django_content_type, django_migrations, django_session, django_admin_log, captcha_captchastore
- 项目业务表(24张):django_dept, django_menu, django_role, django_user, django_city 等