# 数据库迁移问题根因分析与解决方案 **日期**: 2026-02-14 **问题**: 反复遇到数据库迁移失败 **影响**: 应用无法启动,API 返回 500 错误 --- ## 🔍 根本原因 ### 核心问题:**不规范的迁移文件创建方式** 项目中存在两种迁移文件格式混用,导致迁移链混乱: #### ❌ **手动创建(错误方式)** ```python # 文件名: 20260214_1214_add_thumbnail_url_to_storyboard_videos.py revision = '20260214_1214' # 使用时间戳作为 revision ID down_revision = '7b87dd948cd7' ``` **问题**: - ❌ revision ID 不唯一,容易重复 - ❌ 与 Alembic 标准不兼容 - ❌ 迁移链容易断裂 - ❌ 难以追踪和调试 #### ✅ **Alembic 自动生成(正确方式)** ```python # 文件名: 20260205_1149_2f908ad1a28d_add_missing_table_comments.py revision: str = '2f908ad1a28d' # 12位随机哈希 down_revision: Union[str, None] = 'ddc84535ab31' ``` **优点**: - ✅ revision ID 唯一且随机 - ✅ 符合 Alembic 标准 - ✅ 自动管理迁移链 - ✅ 易于追踪和回滚 --- ## 📊 问题统计 检查发现项目中的迁移文件格式: ```bash $ grep "^revision = " *.py | wc -l ``` **非标准格式**:约 18 个文件使用时间戳或描述性名称作为 revision ID **标准格式**:仅 5 个文件使用 Alembic 生成的哈希 ID --- ## ✅ 解决方案 ### 立即修复(应急方案) 当遇到迁移失败时: ```bash # 1. 完全重建容器和数据库 cd server ./start_docker.sh -c -b # 2. 等待启动完成 sleep 30 # 3. 验证迁移状态 docker exec jointo-server-app alembic current # 4. 测试 API curl http://localhost:6170/api/health ``` ### 长期解决(规范化) #### 1. **禁止手动创建迁移文件** **错误示例**: ```bash # ❌ 不要这样做 touch alembic/versions/20260214_1214_add_field.py # 然后手动编辑内容 ``` **正确方式**: ```bash # ✅ 始终使用 Alembic 命令 docker exec jointo-server-app alembic revision -m "add thumbnail_url to storyboard_videos" # 或使用 autogenerate(推荐) docker exec jointo-server-app alembic revision --autogenerate -m "add thumbnail_url" ``` #### 2. **迁移文件创建流程** ```bash # Step 1: 修改 Model 文件 # 编辑 app/models/storyboard_resource.py # Step 2: 生成迁移文件 docker exec jointo-server-app alembic revision --autogenerate -m "描述你的变更" # Step 3: 检查生成的迁移文件 # 查看 alembic/versions/ 下最新的文件 # Step 4: 如有必要,手动调整迁移内容 # 但不要修改 revision 和 down_revision # Step 5: 执行迁移 docker exec jointo-server-app alembic upgrade head # Step 6: 验证 docker exec jointo-server-postgres psql -U jointoAI -d jointo -c "\d storyboard_videos" ``` #### 3. **现有迁移文件的修复计划** **不建议修改现有迁移**:已经执行过的迁移不应修改,会导致更多问题。 **推荐方案**: 1. 保持现有迁移文件不变 2. 新的迁移严格使用 Alembic 生成 3. 在项目文档中记录这个历史遗留问题 4. 定期备份数据库 --- ## 📋 最佳实践 ### 开发流程规范 #### 添加新字段 ```python # 1. 修改 Model class StoryboardVideo(SQLModel, table=True): # ... 现有字段 ... thumbnail_url: Optional[str] = Field( default=None, max_length=500, sa_column_kwargs={"comment": "缩略图URL(视频第一帧)"} ) # 2. 生成迁移 $ docker exec jointo-server-app alembic revision --autogenerate -m "add thumbnail_url to storyboard_videos" # 3. 检查生成的文件 # alembic/versions/20260214_1310_abc123def456_add_thumbnail_url_to_storyboard_videos.py # 4. 执行迁移 $ docker exec jointo-server-app alembic upgrade head ``` #### 删除字段 ```python # 1. 从 Model 中删除字段 # 2. 生成迁移 $ docker exec jointo-server-app alembic revision --autogenerate -m "remove obsolete_field" # 3. 手动编辑迁移文件,确保 downgrade() 能恢复数据 def downgrade() -> None: op.add_column('table_name', sa.Column('obsolete_field', ...)) # 4. 执行迁移 $ docker exec jointo-server-app alembic upgrade head ``` #### 修改字段 ```python # 1. 修改 Model 字段定义 # 2. 生成迁移 $ docker exec jointo-server-app alembic revision --autogenerate -m "change field_name type" # 3. **重要**:检查生成的迁移,autogenerate 可能无法检测某些变更 # 必要时手动添加 ALTER COLUMN 语句 # 4. 执行迁移 $ docker exec jointo-server-app alembic upgrade head ``` ### 迁移测试流程 每次创建迁移后,必须测试: ```bash # 1. 测试 upgrade docker exec jointo-server-app alembic upgrade head # 2. 测试 downgrade docker exec jointo-server-app alembic downgrade -1 # 3. 再次 upgrade(确保可以重复执行) docker exec jointo-server-app alembic upgrade head # 4. 在干净环境测试(最重要!) ./start_docker.sh -c -b # 完全重建 # 等待启动,查看日志确认迁移成功 ``` --- ## 🚫 常见错误 ### 错误 1:手动创建迁移文件 ```bash # ❌ 错误 vi alembic/versions/add_new_field.py # ✅ 正确 docker exec jointo-server-app alembic revision -m "add new field" ``` ### 错误 2:修改已执行的迁移 ```python # ❌ 错误:修改已经执行过的迁移文件 # alembic/versions/20260213_1840_xxx.py revision = '20260213_1840' # 已经在数据库中记录 # 然后修改 upgrade() 内容 # ✅ 正确:创建新的迁移 docker exec jointo-server-app alembic revision -m "fix previous migration" ``` ### 错误 3:忘记删除 __pycache__ ```bash # 删除文件后必须清理缓存 rm alembic/versions/some_migration.py rm -rf alembic/versions/__pycache__/ docker restart jointo-server-app ``` ### 错误 4:不测试干净环境 ```bash # ❌ 错误:只在现有数据库测试 docker exec jointo-server-app alembic upgrade head # 看起来成功了! # ✅ 正确:在干净环境测试 ./start_docker.sh -c -b # 确认从零开始能完整执行所有迁移 ``` --- ## 🛠️ 调试工具 ### 查看迁移历史 ```bash # 查看当前版本 docker exec jointo-server-app alembic current # 查看迁移历史 docker exec jointo-server-app alembic history # 查看详细历史 docker exec jointo-server-app alembic history --verbose # 查看所有 head docker exec jointo-server-app alembic heads ``` ### 检查迁移链 ```bash # 检查是否有分支 docker exec jointo-server-app alembic branches # 检查未应用的迁移 docker exec jointo-server-app alembic show head ``` ### 数据库检查 ```bash # 查看所有表 docker exec jointo-server-postgres psql -U jointoAI -d jointo -c "\dt" # 查看表结构 docker exec jointo-server-postgres psql -U jointoAI -d jointo -c "\d storyboard_videos" # 查看迁移版本记录 docker exec jointo-server-postgres psql -U jointoAI -d jointo -c "SELECT * FROM alembic_version;" ``` --- ## 📚 相关文档 - [Alembic 官方文档](https://alembic.sqlalchemy.org/) - [ADR 006: TIMESTAMPTZ 时间戳规范](/docs/architecture/adrs/006-timestamptz-standard.md) - [数据库迁移指南](/docs/server/guides/database-migration.md) --- ## 🎯 总结 ### 问题根源 **混合使用手动创建和 Alembic 生成的迁移文件,导致迁移链混乱** ### 解决方案 1. ✅ **永远使用 `alembic revision` 命令生成迁移** 2. ✅ **不要手动创建或修改 revision ID** 3. ✅ **每次迁移后在干净环境测试** 4. ✅ **定期清理 __pycache__** ### 紧急恢复 ```bash ./start_docker.sh -c -b ``` 遵循这些规范,可以避免 99% 的迁移问题!