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.6 KiB

Alembic 迁移系统实施

日期: 2026-01-27
类型: 基础设施改进
影响范围: 数据库迁移工具

概述

将项目的数据库迁移系统从自定义脚本迁移到 Alembic,提供更强大的版本管理、自动生成和回滚功能。

变更内容

1. 新增文件

配置文件

  • server/alembic.ini - Alembic 主配置文件
  • server/alembic/env.py - 环境配置,支持异步数据库
  • server/alembic/script.py.mako - 迁移文件模板
  • server/alembic/versions/.gitkeep - 版本目录占位

工具脚本

  • server/scripts/db_migrate.py - 便捷迁移命令封装

文档

  • docs/server/guides/alembic-migration-guide.md - 完整使用指南

2. 修改文件

迁移脚本

  • server/migrate_db.sh - 更新为使用 Alembic

技术栈文档

  • .claude/skills/jointo-tech-stack/references/database.md - 更新迁移管理部分

3. 保留文件

  • server/app/migrations/ - 旧迁移文件保留作为参考
  • server/run_migration.py - 保留但标记为 deprecated

功能特性

1. 自动生成迁移

# 自动检测模型变更并生成迁移脚本
python scripts/db_migrate.py create "描述" --autogenerate

2. 版本管理

  • 基于 Git 风格的版本链
  • 支持分支合并
  • 自动依赖管理

3. 回滚支持

# 回滚一个版本
python scripts/db_migrate.py downgrade

# 回滚到指定版本
python scripts/db_migrate.py downgrade <版本号>

# 回滚所有
python scripts/db_migrate.py downgrade base

4. 状态查询

# 当前版本
python scripts/db_migrate.py current

# 迁移历史
python scripts/db_migrate.py history

# 最新版本
python scripts/db_migrate.py heads

技术实现

1. 异步支持

alembic/env.py 配置了异步数据库引擎:

async def run_async_migrations() -> None:
    """在线模式运行异步迁移"""
    connectable = engine

    async with connectable.connect() as connection:
        await connection.run_sync(do_run_migrations)

    await connectable.dispose()

2. 模型导入

自动导入所有 SQLModel 模型以支持 autogenerate:

from app.models.user import User, UserSession
from app.models.folder import Folder
from app.models.project import Project, ProjectMember
from app.models.credit import CreditTransaction, CreditConsumptionLog
# ... 更多模型

3. Jointo 规范集成

禁止外键约束

def render_item(type_, obj, autogen_context):
    """自定义渲染逻辑"""
    # 禁止自动生成外键约束(Jointo 规范)
    if type_ == "foreign_key":
        return False
    return False

过滤对象

def include_object(object, name, type_, reflected, compare_to):
    """过滤要包含的数据库对象"""
    # 忽略 Alembic 自己的版本表
    if type_ == "table" and name == "alembic_version":
        return False
    # 忽略旧的迁移记录表
    if type_ == "table" and name == "schema_migrations":
        return False
    return True

4. 便捷脚本

scripts/db_migrate.py 提供友好的命令行界面:

  • 彩色输出
  • 详细帮助信息
  • 安全确认(回滚操作)
  • 错误处理

使用示例

创建新迁移

# 1. 修改模型
# server/app/models/user.py
class User(SQLModel, table=True):
    user_id: UUID = Field(default_factory=uuid7, primary_key=True)
    username: str = Field(max_length=255, unique=True)
    phone: Optional[str] = Field(default=None, max_length=20)  # 新增

# 2. 生成迁移
python scripts/db_migrate.py create "添加用户手机号字段" --autogenerate

# 3. 检查生成的文件
# server/alembic/versions/20260127_1430_abc123_添加用户手机号字段.py

# 4. 执行迁移
python scripts/db_migrate.py upgrade

回滚迁移

# 回滚一个版本
python scripts/db_migrate.py downgrade

# 确认提示
# 警告:此操作可能导致数据丢失
# 确认继续?(yes/no): yes

查看状态

# 当前版本
$ python scripts/db_migrate.py current
INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
Current revision for postgresql://...: abc123 (head)

# 迁移历史
$ python scripts/db_migrate.py history
abc123 -> xyz789 (head), 添加用户手机号字段
xyz789 -> def456, 创建项目表
def456 -> <base>, 初始迁移

迁移路径

从旧系统迁移

如果数据库已使用旧迁移系统:

# 1. 创建基线迁移
python scripts/db_migrate.py create "baseline" --autogenerate

# 2. 标记为已应用(不执行)
python scripts/db_migrate.py stamp head

# 3. 验证
python scripts/db_migrate.py current

新项目

直接使用 Alembic:

# 1. 创建初始迁移
python scripts/db_migrate.py create "initial" --autogenerate

# 2. 执行迁移
python scripts/db_migrate.py upgrade

最佳实践

1. 开发流程

  1. 修改模型
  2. 生成迁移:create "描述" --autogenerate
  3. 检查生成的代码
  4. 测试 upgrade 和 downgrade
  5. 提交代码

2. 命名规范

  • 使用动词开头:添加修改删除
  • 包含表名和字段名
  • 清晰明确

3. 代码审查

  • 检查是否符合 Jointo 规范
  • 验证 downgrade 逻辑
  • 确认数据迁移安全

4. 生产部署

# 1. 备份数据库
pg_dump -U postgres jointo > backup.sql

# 2. 执行迁移
python scripts/db_migrate.py upgrade

# 3. 验证
python scripts/db_migrate.py current

优势对比

功能 旧系统 Alembic
自动生成
版本管理 ⚠️ 手动 自动
回滚支持 ⚠️ 不完整 完整
模型同步检测
分支合并
依赖管理
状态查询 ⚠️ 基础 详细
社区支持

注意事项

1. 外键约束

Alembic 默认会生成外键约束,但 Jointo 规范禁止使用。已在 env.py 中配置自动过滤:

def render_item(type_, obj, autogen_context):
    if type_ == "foreign_key":
        return False  # 禁止生成外键
    return False

2. 枚举类型

使用 SMALLINT 而非 PostgreSQL ENUM:

# ✅ 正确
sa.Column('status', sa.SmallInteger(), nullable=False)

# ❌ 错误
sa.Column('status', sa.Enum('active', 'archived', name='status'), nullable=False)

3. UUID v7

确保使用 UUID v7 函数:

sa.Column('id', sa.UUID(), server_default=sa.text('gen_uuid_v7()'), nullable=False)

4. 时间戳

使用 TIMESTAMPTZ 和触发器:

sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False)
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False)

后续计划

短期

  • 创建基线迁移标记当前数据库状态
  • 归档旧迁移文件到 .archive/
  • 更新部署文档

中期

  • 添加迁移测试自动化
  • 集成到 CI/CD 流程
  • 添加迁移性能监控

长期

  • 探索零停机迁移策略
  • 实现迁移回滚自动化
  • 建立迁移最佳实践库

参考资料

相关文档