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 迁移文件结构关系图
下图展示了迁移文件命名、目录结构与内部模板之间的整体关系,帮助团队建立统一的认知模型。
3.5 分支策略与目录的协同
在 Git 工作流中,不同分支的迁移文件应遵循以下目录协同规则,避免版本号冲突和合并困难:
- 主分支(main/master):只包含已发布的正式版本号文件,如
V1.0.0__、V1.1.0__。 - 功能分支(feature/*):使用时间戳命名,如
V20250321_1430__,确保分支间不冲突。 - 合并前:在功能分支上重新编号,将时间戳改为目标版本号,并确保与主分支的版本号连续。
- 回滚脚本:始终与正向迁移文件在同一分支提交,禁止跨分支引用回滚脚本。
最佳实践:在 CI/CD 流水线中增加迁移文件命名检查步骤,自动校验文件名是否符合规范、版本号是否重复、回滚脚本是否存在。这可以在 Code Review 之前拦截大部分结构性问题。