You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

7.5 KiB

数据库迁移问题根因分析与解决方案

日期: 2026-02-14
问题: 反复遇到数据库迁移失败
影响: 应用无法启动,API 返回 500 错误


🔍 根本原因

核心问题:不规范的迁移文件创建方式

项目中存在两种迁移文件格式混用,导致迁移链混乱:

手动创建(错误方式)

# 文件名: 20260214_1214_add_thumbnail_url_to_storyboard_videos.py
revision = '20260214_1214'          # 使用时间戳作为 revision ID
down_revision = '7b87dd948cd7'

问题

  • revision ID 不唯一,容易重复
  • 与 Alembic 标准不兼容
  • 迁移链容易断裂
  • 难以追踪和调试

Alembic 自动生成(正确方式)

# 文件名: 20260205_1149_2f908ad1a28d_add_missing_table_comments.py
revision: str = '2f908ad1a28d'      # 12位随机哈希
down_revision: Union[str, None] = 'ddc84535ab31'

优点

  • revision ID 唯一且随机
  • 符合 Alembic 标准
  • 自动管理迁移链
  • 易于追踪和回滚

📊 问题统计

检查发现项目中的迁移文件格式:

$ grep "^revision = " *.py | wc -l

非标准格式:约 18 个文件使用时间戳或描述性名称作为 revision ID
标准格式:仅 5 个文件使用 Alembic 生成的哈希 ID


解决方案

立即修复(应急方案)

当遇到迁移失败时:

# 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. 禁止手动创建迁移文件

错误示例

# ❌ 不要这样做
touch alembic/versions/20260214_1214_add_field.py
# 然后手动编辑内容

正确方式

# ✅ 始终使用 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. 迁移文件创建流程

# 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. 定期备份数据库

📋 最佳实践

开发流程规范

添加新字段

# 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

删除字段

# 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

修改字段

# 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

迁移测试流程

每次创建迁移后,必须测试:

# 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:手动创建迁移文件

# ❌ 错误
vi alembic/versions/add_new_field.py

# ✅ 正确
docker exec jointo-server-app alembic revision -m "add new field"

错误 2:修改已执行的迁移

# ❌ 错误:修改已经执行过的迁移文件
# alembic/versions/20260213_1840_xxx.py
revision = '20260213_1840'  # 已经在数据库中记录
# 然后修改 upgrade() 内容

# ✅ 正确:创建新的迁移
docker exec jointo-server-app alembic revision -m "fix previous migration"

错误 3:忘记删除 pycache

# 删除文件后必须清理缓存
rm alembic/versions/some_migration.py
rm -rf alembic/versions/__pycache__/
docker restart jointo-server-app

错误 4:不测试干净环境

# ❌ 错误:只在现有数据库测试
docker exec jointo-server-app alembic upgrade head
# 看起来成功了!

# ✅ 正确:在干净环境测试
./start_docker.sh -c -b
# 确认从零开始能完整执行所有迁移

🛠️ 调试工具

查看迁移历史

# 查看当前版本
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

检查迁移链

# 检查是否有分支
docker exec jointo-server-app alembic branches

# 检查未应用的迁移
docker exec jointo-server-app alembic show head

数据库检查

# 查看所有表
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 生成的迁移文件,导致迁移链混乱

解决方案

  1. 永远使用 alembic revision 命令生成迁移
  2. 不要手动创建或修改 revision ID
  3. 每次迁移后在干净环境测试
  4. 定期清理 pycache

紧急恢复

./start_docker.sh -c -b

遵循这些规范,可以避免 99% 的迁移问题!