110 lines
2.8 KiB
Markdown
110 lines
2.8 KiB
Markdown
# MySQL 8.0.35 迁移说明
|
||
|
||
## 当前状态
|
||
|
||
生产数据库目标为 MySQL 8.0.35:
|
||
|
||
- Django 驱动:`mysqlclient`
|
||
- 字符集:`utf8mb4`
|
||
- 排序规则:`utf8mb4_0900_ai_ci`
|
||
- 存储引擎:InnoDB
|
||
- 事务隔离:READ COMMITTED
|
||
- SQL 模式:STRICT_TRANS_TABLES
|
||
- 实时匹配:MySQL 8 `SELECT ... FOR UPDATE SKIP LOCKED`
|
||
|
||
MySQL 5.7 不受支持。生产检查会拒绝版本低于 8.0.35 的数据库。
|
||
|
||
## 新空库
|
||
|
||
新安装的 MySQL 8 直接执行:
|
||
|
||
```bash
|
||
set -a
|
||
source .env.production
|
||
set +a
|
||
|
||
.venv-production/bin/python backend/manage.py check_mysql
|
||
.venv-production/bin/python backend/manage.py migrate --noinput
|
||
.venv-production/bin/python backend/manage.py seed_initial_content
|
||
.venv-production/bin/python backend/manage.py seed_contests
|
||
```
|
||
|
||
## 从当前 Django SQLite 搬数据
|
||
|
||
仅适用于本仓库 Django 创建的 `backend/db.sqlite3`,不适用于旧 Flask 的 `miniapp_v3.db`。
|
||
|
||
先在旧 SQLite 环境导出业务数据:
|
||
|
||
```bash
|
||
unset DATABASE_URL
|
||
|
||
.venv/bin/python backend/manage.py dumpdata \
|
||
--natural-foreign \
|
||
--natural-primary \
|
||
--exclude contenttypes \
|
||
--exclude auth.permission \
|
||
--exclude admin.logentry \
|
||
--exclude sessions.session \
|
||
--indent 2 \
|
||
--output /tmp/hulumath-django-data.json
|
||
```
|
||
|
||
切换到空 MySQL 8 数据库:
|
||
|
||
```bash
|
||
set -a
|
||
source .env.production
|
||
set +a
|
||
|
||
.venv-production/bin/python backend/manage.py check_mysql
|
||
.venv-production/bin/python backend/manage.py migrate --noinput
|
||
.venv-production/bin/python backend/manage.py loaddata /tmp/hulumath-django-data.json
|
||
```
|
||
|
||
导入后检查:
|
||
|
||
```bash
|
||
.venv-production/bin/python backend/manage.py shell -c "
|
||
from accounts.models import User
|
||
from math_life.models import StoryRun
|
||
from contest.models import ContestAttempt
|
||
print({
|
||
'users': User.objects.count(),
|
||
'story_runs': StoryRun.objects.count(),
|
||
'contest_attempts': ContestAttempt.objects.count(),
|
||
})
|
||
"
|
||
```
|
||
|
||
确认无误后安全删除中间 JSON,因为其中包含用户资料和密码哈希:
|
||
|
||
```bash
|
||
shred -u /tmp/hulumath-django-data.json
|
||
```
|
||
|
||
## 旧 Flask SQLite
|
||
|
||
旧 Flask `miniapp_v3.db` 的表结构与新 Django 模型不同,不能用 `loaddata`。
|
||
|
||
迁移前必须先确认需要保留的表和字段,再编写一次性 ETL:
|
||
|
||
```text
|
||
读取旧 SQLite
|
||
→ 字段清洗与用户映射
|
||
→ 写入 MySQL Staging
|
||
→ 核对数量和抽样内容
|
||
→ 生产停写窗口
|
||
→ 最终增量迁移
|
||
→ 切换域名
|
||
```
|
||
|
||
如果旧站没有真实用户数据,建议不迁移旧运行数据,直接使用新 MySQL 空库和官方种子。
|
||
|
||
## 不允许的迁移方式
|
||
|
||
- 不复制 MySQL 5.7 数据目录给 MySQL 8。
|
||
- 不把 SQLite 文件放进 MySQL 数据目录。
|
||
- 不直接修改 Django migration 历史记录。
|
||
- 不在没有 `mysqldump` 备份时执行生产迁移。
|
||
- 不让 MySQL 5.7 与 8.0 共用同一数据目录。
|