3. 数据库迁移文件的结构设计

数据库迁移文件是版本协同与代码审查的核心载体。一个清晰、一致的文件结构能够显著降低多人协作时的冲突概率,并让审查者快速理解每次变更的范围与意图。本节将定义迁移文件的命名规范、目录组织方式以及文件内部的标准模板。

3.1 迁移文件命名规范

命名规范需要同时满足三个目标:全局唯一、自然排序、语义可读。推荐采用 Flyway 风格的版本号前缀 + 描述性后缀的组合模式。

命名模式 示例 适用场景
V<主版本>.<次版本>.<修订号>__<描述>.sql V1.0.0__init.sql 正式发布版本,语义化版本号
V<时间戳>__<描述>.sql V20250321_1430__add_user_table.sql 持续开发分支,避免版本号冲突
R__<描述>.sql R__grant_permissions.sql 可重复执行的脚本(视图、函数、权限)
U__<描述>.sql U__fix_index_name.sql 撤销或回滚脚本(Undo)
命名要点:
  • 版本号与描述之间使用双下划线 __ 分隔,避免与文件名中的单下划线混淆。
  • 描述部分使用小写字母 + 下划线,保持简洁,不超过 50 个字符。
  • 禁止在文件名中使用空格、中文或特殊符号。

3.2 目录结构设计

推荐采用分层目录结构,将迁移文件、配置、回滚脚本和工具类脚本分离,便于 Git 管理和 CI/CD 集成。

project-root/
├── migrations/
│   ├── versions/                  # 核心迁移文件目录
│   │   ├── V1.0.0__init.sql
│   │   ├── V1.0.1__add_user_table.sql
│   │   ├── V1.1.0__create_orders.sql
│   │   └── V1.1.1__add_order_index.sql
│   ├── undo/                      # 回滚脚本目录(与 versions 一一对应)
│   │   ├── U1.0.1__add_user_table.sql
│   │   └── U1.1.0__create_orders.sql
│   ├── repeatable/                # 可重复执行脚本
│   │   ├── R__view_active_users.sql
│   │   └── R__function_calc_total.sql
│   ├── seed/                      # 种子数据脚本
│   │   └── seed_initial_data.sql
│   └── config/                    # 迁移工具配置
│       ├── flyway.conf
│       └── migration_policy.yml
└── .gitignore
目录职责说明:
  • versions/:存放所有版本化迁移文件,按版本号或时间戳自然排序。
  • undo/:存放与 versions 对应的回滚脚本,命名前缀为 U,版本号与正向迁移一致。
  • repeatable/:存放视图、函数、存储过程等需要反复执行的脚本,每次修改后重新执行。
  • seed/:存放环境初始化或测试所需的静态数据。
  • config/:存放迁移工具的全局配置,纳入版本控制以保持环境一致性。

3.3 迁移文件内部结构模板

每个迁移文件应遵循统一的内部结构,包含元信息注释、变更内容、回滚指引和校验检查。以下是一个标准模板:

-- ============================================================
-- Migration: V1.0.1__add_user_table
-- Author:    Zhang Wei
-- Date:      2025-03-21
-- JIRA:      PROJ-1234
-- Description: 创建用户基础表,包含用户名、邮箱和注册时间。
-- ============================================================

-- 1. 前置检查:确保表不存在
DO $$
BEGIN
    IF EXISTS (SELECT 1 FROM information_schema.tables
               WHERE table_name = 'users') THEN
        RAISE EXCEPTION 'Table users already exists';
    END IF;
END $$;

-- 2. 执行变更
CREATE TABLE users (
    id          BIGSERIAL PRIMARY KEY,
    username    VARCHAR(64)  NOT NULL UNIQUE,
    email       VARCHAR(255) NOT NULL UNIQUE,
    created_at  TIMESTAMPTZ  NOT NULL DEFAULT NOW(),
    updated_at  TIMESTAMPTZ  NOT NULL DEFAULT NOW()
);

-- 3. 创建索引
CREATE INDEX idx_users_email ON users (email);

-- 4. 添加注释
COMMENT ON TABLE users IS '用户基础信息表';
COMMENT ON COLUMN users.username IS '用户名,唯一标识';
COMMENT ON COLUMN users.email IS '电子邮箱,用于登录和通知';

-- 5. 后置校验:验证表结构
DO $$
BEGIN
    ASSERT (SELECT COUNT(*) = 4 FROM information_schema.columns
            WHERE table_name = 'users'), 'Column count mismatch';
END $$;

-- ============================================================
-- Rollback: U1.0.1__add_user_table.sql
-- DROP TABLE IF EXISTS users CASCADE;
-- ============================================================
重要提醒:
  • 每个迁移文件必须包含回滚脚本的完整路径或 SQL 语句,确保回滚操作可追溯。
  • 前置检查和后置校验是防止重复执行和数据不一致的关键防线,不可省略。
  • 元信息中的 JIRA 或任务编号便于在 Code Review 时关联需求上下文。

3.4 迁移文件结构关系图

下图展示了迁移文件命名、目录结构与内部模板之间的整体关系,帮助团队建立统一的认知模型。

迁移文件结构关系图 命名规范 V1.0.0__init.sql V20250321_1430__add_table.sql R__grant_permissions.sql U__fix_index_name.sql 目录结构 migrations/versions/ migrations/undo/ migrations/repeatable/ migrations/seed/ 内部模板 元信息注释 前置检查 变更 SQL 后置校验 + 回滚指引 核心原则 1. 命名保证全局唯一和自然排序,避免多人同时提交时产生冲突。 2. 目录分层清晰,versions/undo/repeatable/seed 各司其职。 编写迁移文件 Git 提交 + PR Code Review 合并执行 图:迁移文件从编写到合并的完整链路,命名与目录结构贯穿始终

3.5 分支策略与目录的协同

在 Git 工作流中,不同分支的迁移文件应遵循以下目录协同规则,避免版本号冲突和合并困难:

  • 主分支(main/master):只包含已发布的正式版本号文件,如 V1.0.0__、V1.1.0__。
  • 功能分支(feature/*):使用时间戳命名,如 V20250321_1430__,确保分支间不冲突。
  • 合并前:在功能分支上重新编号,将时间戳改为目标版本号,并确保与主分支的版本号连续。
  • 回滚脚本:始终与正向迁移文件在同一分支提交,禁止跨分支引用回滚脚本。
最佳实践:在 CI/CD 流水线中增加迁移文件命名检查步骤,自动校验文件名是否符合规范、版本号是否重复、回滚脚本是否存在。这可以在 Code Review 之前拦截大部分结构性问题。