数据库迁移设计(golang-migrate)
用版本化 up/down SQL 管理 schema:与 GORM AutoMigrate 的分工、迁移文件组织、启动执行策略与回滚边界。
#type / concept
#status / growing
#tech / dev / backend
#resource / go
[!info] 关联笔记
数据库迁移设计(golang-migrate)
这个概念为什么出现
多人协作改表时若靠:
- 手工执行 SQL → 环境不一致
AutoMigrate→ 难删列、难改类型、难做数据迁移
版本化迁移让每次 schema 变更有编号、可审查、可在 CI/CD 重放,并在理想情况下可回滚。
[!abstract] 一句话理解 每个变更一对
up/downSQL(或等价脚本),工具记录已应用版本;应用启动或发布流水线执行up,出问题再评估down。
最小文件布局
migrations/
000001_init.up.sql
000001_init.down.sql
000002_add_users_email.up.sql
000002_add_users_email.down.sql
-- 000001_init.up.sql
CREATE TABLE users (
id UUID PRIMARY KEY,
name TEXT NOT NULL
);
-- 000001_init.down.sql
DROP TABLE IF EXISTS users;
工具示例:golang-migrate(社区主流之一,非标准库)。
migrate -path ./migrations -database "$DATABASE_URL" up
migrate -path ./migrations -database "$DATABASE_URL" version
核心概念
版本表
工具在库中维护 schema_migrations(名因工具而异),记录当前版本,避免重复执行。
up / down
| up | down | |
|---|---|---|
| 含义 | 应用变更 | 撤销变更 |
| 生产 | 常规路径 | 常难完美,需评估数据损失 |
并非所有迁移都值得 down(删表、不可逆数据转换)。文档写清风险。
与 AutoMigrate
| 迁移文件 | AutoMigrate | |
|---|---|---|
| 审查 | Git diff SQL | 隐式 |
| 删列/改类型 | 显式 | 弱/不可 |
| 数据回填 | 可写 SQL | 不适合 |
| 原型速度 | 慢一点 | 快 |
推荐:开发可 AutoMigrate 探索,发布路径用版本迁移。
何时执行
- CD 独立 Job:先 migrate 再滚新版本(推荐)
- 应用启动时:简单但多副本并发需工具锁
- 禁止每个实例无锁乱跑
设计动机
基础设施即代码:schema 与应用版本协同,可重建环境。
边界与反直觉
- 锁与并发:多副本启动 migrate 需保证单飞。
- 长锁迁移:大表
ALTER可能锁表,需在线策略(pt-osc 等,因库而异)。 - expand/contract:先加列兼容旧代码,再切读,再删旧列——跨多版本发布。
- 种子数据与迁移分离,避免环境脏耦合。
常见误区
[!warning] 只靠 AutoMigrate 上生产
缺少可审 SQL 与可控回滚。
[!warning] 编辑已发布的旧迁移文件
已上环境的版本应不可变;再开新版本。
[!warning] down 当儿戏
生产回滚先备份;很多团队只 forward-fix。
工程实践
- 文件名单调递增、团队统一编号规则
- PR 必须含迁移与回滚说明
- CI 对空库
up全量跑通 - 与应用兼容:先部署兼容旧 schema 的代码,或先迁移再部署(按变更类型)
- 权限:迁移账号与运行时账号分离
可验证实验
- 空库连续
up,查版本表。 - 再
up应 no-op。 - 新开
000003,观察只应用增量。 - 故意改已应用文件,理解 checksum 失败(若工具支持)。
本节总结
- 本质:schema 变更的版本控制。
- 关键:不可变历史、发布路径显式、与 ORM 分工。
- 下一步:database-sql-in-go 仓储实现;go-docker-deployment 中挂迁移 Job。
自测题
- 为什么已合并的迁移文件不应改内容?
- expand/contract 解决什么发布问题?
- 多副本同时 migrate 的风险?
答案
- 已部署环境 checksum/版本历史会冲突。
- 零停机下新旧代码并存时的列兼容。
- 竞态执行、锁失败、重复或半应用状态。
延伸阅读
笔记元信息
- 文件:
database-migration-golang-migrate.md - 状态:已深化