本文目录导读:

迁移是一种用于管理数据库结构变更(Schema 变更)的成熟方法,它的核心思想是将数据库的每一次变更(如创建表、添加字段、修改索引)都视为一次代码级别的“版本记录”,像 Git 管理代码一样管理数据库。
以下是关于如何使用迁移管理数据库变更的详细指南,涵盖了核心概念、常用工具、工作流程和最佳实践。
核心概念
- 迁移文件:一个包含 SQL 或特定代码的文件,描述了一次具体的数据库变更。
- Up 操作:执行变更(
ALTER TABLE users ADD COLUMN age INT;)。 - Down 操作:回滚变更(
ALTER TABLE users DROP COLUMN age;)。
- Up 操作:执行变更(
- 版本表:数据库中专门用于记录哪些迁移文件已经被执行过的表(通常叫
_migrations或schema_migrations)。 - 状态:
- 待定:迁移文件已创建,但尚未应用到数据库。
- 已应用:迁移文件已成功执行,并在版本表中记录了版本号。
- 回滚:撤销已应用的迁移。
为什么使用迁移?
- 版本控制:数据库变更与应用程序代码一起纳入 Git 等版本控制系统,实现了真正的“基础设施即代码”。
- 可追溯:任何人都可以查看迁移历史,清楚地知道数据库从创建到现在经历的所有变更。
- 可复现:从零开始,只需运行所有迁移文件,就能构建出与生产环境一致的数据库结构。
- 团队协作:团队成员无需手动同步数据库,只需拉取代码并运行迁移命令即可。
- 可回滚:如果新版本出现问题,可以快速、安全地回滚到前一个数据库状态。
常用迁移工具(按语言/环境分类)
- 通用 / 云原生:
- Flyway:基于 Java,但兼容性极强,支持 SQL 脚本,社区非常活跃。
- Liquibase:基于 XML/YAML/JSON/SQL 描述变更,功能强大,适合复杂企业级场景。
- 后端框架内置:
- Ruby on Rails:Active Record Migrations(最经典的实现)。
- Django:
python manage.py makemigrations和migrate。 - Laravel:
php artisan make:migration。 - Node.js (Sequelize, TypeORM, Prisma):各有自己的迁移系统。
- Go (golang-migrate, goose):常用的数据库无关的迁移库。
- Python:Alembic(专为 SQLAlchemy 设计)。
- 数据库管理工具:
- Sqitch:纯 SQL,应用名称“版本控制数据库”,设计哲学非常独特。
通用工作流程(以 Flyway 或通用模式为例)
假设你有一个使用 Flyway 管理的项目,数据库是 PostgreSQL。
步骤 1:初始化项目
在你的项目中创建一个目录(如 db/migrations),用于存放所有迁移文件。
步骤 2:创建你的第一个迁移文件
文件命名规则通常包含版本号、描述和操作(如 up/down),Flyway 的通用命名是 V<版本号>__<描述>.sql。
- 文件名:
V1__create_users_table.sql - (Up):
CREATE TABLE users ( id BIGSERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, email VARCHAR(255) UNIQUE NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT NOW() ); - (Down):
DROP TABLE IF EXISTS users;
注意:Flyway 社区版原生支持 Up 和 Down 脚本的方式,但通常它直接通过 undo 命令或多文件模式处理回滚,最简方式是 Up 文件单独存在,Down 文件额外创建。
步骤 3:应用迁移
运行命令 flyway migrate,Flyway 会:
- 检查数据库的
flyway_schema_history表(如果不存在则创建)。 - 比较本地迁移文件版本和已执行的版本。
- 按顺序执行所有未执行的迁移文件(
V1__...)。 - 记录
V1已执行,变更生效。
步骤 4:团队协作
- 团队成员
git pull获取最新的V1__create_users_table.sql。 - 他们运行
flyway migrate(或者flyway info查看待定状态),系统会自动应用缺失的迁移。 - 数据库结构保持一致。
步骤 5:处理变更迭代
你需要添加一个 age 字段。
- 文件 2:
V2__add_age_to_users.sql - Up:
ALTER TABLE users ADD COLUMN age INT NOT NULL DEFAULT 0;
- Down:
ALTER TABLE users DROP COLUMN age;
- 再次运行
flyway migrate,系统会识别出V1已应用,只执行V2。
步骤 6:回滚(如果出现问题)
假设你发现 V2 的修改存在错误。
- 运行
flyway undo(如果工具支持)或手动执行V2__add_age_to_users.sql中 Down 部分的 SQL。 - 数据库恢复为
V1的状态。 - 你可以修改
V2的代码,或者创建一个V3来修复问题。
最佳实践与注意事项
- 一次只改一件事:每个迁移文件应该只完成一个逻辑变更(只添加一个字段,而不是同时添加字段和修改索引),这有助于排查问题和回滚。
- 永远不要修改已合并到主分支的迁移文件:迁移文件是“只追加”的,如果你需要修改一个已上线的变更,请创建一个新的迁移文件(
V3__fix_age_column_type.sql),而不是去改V2,否则会导致版本校验失败,团队同事也会遇到冲突。 - 保持迁移文件幂等(如果需要):对于像
Flyway这样支持重复迁移的工具,可以编写幂等的 SQL(CREATE TABLE IF NOT EXISTS),但通常不建议依赖这一点,最好明确版本。 - 测试 Down 迁移:在开发环境中,确保你的 Down 脚本真的能够成功撤销 Up 操作,并且不会丢失关键业务数据(Down 脚本会备份或确认无数据冲突)。
- 自动化 CI/CD 集成:
- 在持续集成流水线中,每次代码提交后,可以使用测试数据库运行所有的迁移,以确保没有冲突或 SQL 错误。
- 在生产环境部署时,将迁移作为部署流水线的一个步骤(先迁移数据库,再部署新应用代码,或反过来,取决于具体情况,但通常建议先迁移代码兼容,再迁移数据库结构或先迁移数据库结构,再部署新代码)。
- 处理数据迁移:迁移也适合处理数据变更(如:
UPDATE users SET status='active' WHERE status IS NULL),但要注意,这种迁移需要更谨慎,因为回滚可能更复杂(需要UPDATE回去)。 - 使用代码版本:迁移文件的版本号通常与代码版本无关,是连续的整数或时间戳,不要用 Git 的 Commit ID 直接用,容易混乱。
| 传统手动变更 | 使用迁移管理 |
|---|---|
在数据库命令行直接 ALTER TABLE |
在 IDE 中写 V2__...sql 文件 |
| 团队不知道谁改了 | 代码版本控制,有历史记录 |
| 新同事需要手动导入 SQL 文件 | 运行 migrate 命令自动构建 |
| 回滚需要去备份恢复 | 运行 undo 或对应的 Down 脚本 |
| 部署时容易遗漏或顺序错误 | 按顺序自动执行,有校验机制 |
一句话总结:迁移就是“将数据库的变更做成代码版本,用工具来按顺序执行和回滚”,选一个你项目语言生态里成熟、文档全的工具(如 Flyway 或框架自带的),严格遵循“只追加不修改”的原则,配合 CI/CD 自动化,就能稳定地驾驭数据库变更。