Schema 迁移指南
Issue: #2190
本文档说明 Open ACE 的 schema 迁移策略、最佳实践和故障排查方法。
Schema 权威模型
Open ACE 采用 Alembic 作为唯一 schema 变更通道 的权威模型:
┌───────────────────────────────────── ────────────────────────┐
│ Schema 变更权威模型 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 生产环境:alembic upgrade head 是唯一 schema 变更通道 │
│ │
│ 开发环境:SQLite 允许 bootstrap,但必须与生产路径分离 │
│ │
│ 禁止:create_app() 对 PostgreSQL 执行 DDL │
│ 运行时自动补列/ALTER TABLE │
│ 业务代码发现缺列后修 schema │
└────────────────────────────────────────── ───────────────────┘
迁移前的准备工作
1. 检查当前数据库版本
alembic current
输出示例:
20260801_001_add_platform_tenant_admin_roles (head)
2. 查看待执行的 migrations
alembic history --verbose
3. 备份数据库
# PostgreSQL
pg_dump -h localhost -U open-ace ace > backup_$(date +%Y%m%d).sql
# SQLite
cp open-ace.db open-ace.db.backup
执行迁移
标准升级流程
# 1. 检查当前版本
alembic current
# 2. 执行升级
alembic upgrade head
# 3. 验证升级成功
alembic current # 应显示最新 revision
# 4. 验证 schema 完整性
python3 scripts/verify_schema_integrity.py
降级(回滚)
# 回退一个版本
alembic downgrade -1
# 回退到指定版本
alembic downgrade <revision_id>
# 回退所有 migrations(保留数据库结构)
alembic downgrade base
注意:降级操作会删除列,可能导致数据丢失。生产环境降级前必须备份。
迁移策略
Expand/Contract 模式
对于滚动升级场景,采用 expand/contract migration 模式:
阶段 1: Expand(扩展)
添加可空列或有默认值的列:
# migration: add_column_expand.py
def upgrade():
op.add_column(
"users",
sa.Column("new_field", sa.Text(), nullable=True), # 可空
)
特点:
- 新应用可以使用新列
- 旧应用忽略新列,不受影响
- 支持新旧版本共存
阶段 2: 稳定运行
所有 Pod 升级到新版本,新列正常使用。
阶段 3: Contract(收缩,可选)
确认无回退需求后,添加约束或删除旧列:
# migration: add_column_constraint.py
def upgrade():
# 添加 NOT NULL 约束
op.alter_column(
"users",
"new_field",
nullable=False,
server_default="default_value",
)
兼容性窗口
应用版本与 schema 版本的兼容关系:
| 应用版本 | 最小 Schema 版本 | 最大 Schema 版本 | 兼容窗口 |
|---|---|---|---|
| v2.1 | baseline_2026_06_23 | HEAD | 10 revisions |
| v2.0 | baseline_2026_06_23 | 20260717_004 | 5 revisions |
判断逻辑:
- 应用启动时检查 schema 版本是否在兼容窗口内
- 版本过低:启动失败,提示升级
- 版本过高:启动失败,提示应用需要升级
禁止的操作
❌ 生产环境禁止运 行时 DDL
# 错误示例(已禁止)
@app.before_request
def ensure_columns():
# 不要在运行时执行 ALTER TABLE
cursor.execute("ALTER TABLE users ADD COLUMN ...")
❌ 业务代码直接修改 schema
# 错误示例
def create_session():
try:
cursor.execute("INSERT INTO sessions ...")
except ColumnMissingError:
# 不要尝试修复 schema
cursor.execute("ALTER TABLE sessions ADD COLUMN ...")
cursor.execute("INSERT INTO sessions ...")
✅ 正确做法
如果发现缺少列,应该:
- 停止应用
- 执行 migration
- 重新启动应用
故障排查
Migration 执行失败
症状:alembic upgrade head 报错
诊断步骤:
# 1. 检查数据库连接
pg_isready -h <host> -p <port>
# 2. 检查当前版本
alembic current
# 3. 检查 migration 历史
alembic history
# 4. 查看详细错误信息
alembic upgrade head --sql
常见错误及解决方法:
| 错误信息 | 原因 | 解决方法 |
|---|---|---|
Can't locate revision | migration 链断裂 | 检查 down_revision 是否正确 |
relation "xxx" already exists | migration 重复执行 | 使用幂等性检查 |
column "xxx" of relation "xxx" does not exist | 列未添加 | 检查 migration 执行顺序 |
Schema 版本不匹配
症状:应用启动失败,提示 schema 版本过低
解决方法:
# 1. 检查当前版本
alembic current
# 2. 执行升级
alembic upgrade head
# 3. 如果是新数据库,执行完整初始化
alembic upgrade head
python3 scripts/init_db.py # 创建默认用户
多 Pod 并发迁移冲突
症状:多个 Pod 同时启动,执行 migration 时出现锁等待或死锁
解决方法:
方案 1: Kubernetes Init Container
initContainers:
- name: run-migrations
image: open-ace:latest
command: ["alembic", "upgrade", "head"]
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
方案 2: 独立迁移任务
# 在部署应用前,单独运行 migration job
apiVersion: batch/v1
kind: Job
metadata:
name: schema-migration
spec:
template:
spec:
containers:
- name: migrator
image: open-ace:latest
command: ["alembic", "upgrade", "head"]