- 数据库
- 开发工具
- CLI
【免费下载链接】migrate
Database migrations. CLI and Golang library.
这篇指南以 GETTING_STARTED.md 为主体,系统讲解 golang-migrate(即 migrate)从零开始的完整使用流程:创建迁移文件、填充内容、执行迁移、测试回滚,以及处理失败迁移留下的脏(dirty)状态。读者学完后将掌握 migrate CLI 的核心命令与参数、up/down 迁移文件规范,并能结合源码理解底层版本记录与加锁机制,直接用于实际项目。
开始之前:理解 up/down 迁移概念
在动手之前,首先要理解数据库迁移中两个基础方向:
- forward / up:将数据库从当前版本迁移到更新版本,通常执行
CREATE TABLE、ALTER TABLE等变更; - reverse / down:将数据库回退到之前的版本,通常执行
DROP TABLE等反向操作。
migrate 的核心模型是:一个逻辑迁移由一对文件表示——.up文件前进、.down文件回退,二者共享同一个版本号。例如仓库中的真实示例 1085649617_create_users_table.up.sql 与 1085649617_create_users_table.down.sql,up 文件创建users表,down 文件则负责删除它。这一"一对文件"设计的具体规范可参考 MIGRATIONS.md。
其次,为你的应用配置好一个数据库,并确认所使用的数据库驱动已被支持。migrate 的驱动列表维护在 README.md,覆盖 PostgreSQL、PGX(v4/v5)、Redshift、MySQL/MariaDB、SQLite/SQLite3/SQLCipher、Cassandra/ScyllaDB、ClickHouse、CockroachDB、YugabyteDB、MongoDB、Neo4j、SQL Server、Spanner、Firebird、rqlite 等众多数据库。数据库连接串统一使用 URL 形式:dbdriver://username:password@host:port/dbname?param1=true¶m2=false,URL 中的保留字符(如!、#、$、%、@、?等)需要先做百分号编码(Percent-Encoding)。
创建迁移文件
使用 migrate CLI 的create子命令创建迁移。官方示例:
migrate create -ext sql -dir db/migrations -seq create_users_table该命令会在db/migrations目录下生成一对以 6 位序列号开头的文件:
000001_create_users_table.up.sql 000001_create_users_table.down.sql创建后,你只需要向这两个文件中填充 SQL 内容即可。
迁移文件名规范
create生成的文件名遵循 migrate 全局解析规则。在 source/parse.go 中定义了文件名正则:
Regex = regexp.MustCompile(`^([0-9]+)_(.*)\.(up|down)\.(.*)$`)即文件名必须是{version}_{title}.{up|down}.{extension}形式:版本号(任意 64 位无符号整数)、标题(仅作可读性,不参与逻辑)、方向(up/down)、扩展名(如.sql)。所有迁移按版本号升序执行 up,降序执行 down。从源码结构看,Parse函数会提取Version、Identifier、Direction字段,供后续调度使用。
序列号模式与时间戳模式
create支持两种版本生成策略(见 internal/cli/main.go 与 internal/cli/commands.go 的实现):
- 序列模式(-seq):默认 6 位数字(
-digits N可自定义位数),自动取目录中已存在的最大序号并加 1(nextSeqVersion逻辑)。当序号位数不足时会报错提示。 - 时间戳模式(默认):默认使用 Go 时间格式
20060102150405(即YYYYMMDDHHMMSS,可通过-format指定其他 Go 时间格式,或使用特殊值unix/unixNano),时区默认 UTC(可用-tz指定)。若两个迁移落在同一时间戳,createCmd会检测到duplicate migration version错误并拒绝生成。
create的完整参数为:-ext E(扩展名,必填)、-dir D(目录,默认当前工作目录)、-seq、-digits N(默认 6)、-format(时间格式)、-tz(时区)。此外create使用O_EXCL独占模式创建文件,防止覆盖已存在的迁移。
填充迁移内容:幂等性与事务
创建文件之后,重点在于如何写出健壮的迁移内容。官方文档给出了三条关键建议:
1. 关注多开发者协作下的迁移一致性
IMPORTANT:在多人开发的项目中,存在迁移不一致的风险——例如两位开发者创建了冲突的迁移,而后创建迁移的那位开发者反而先合入仓库。开发团队应在代码评审时特别留意这类情况。这是真实世界的工程问题,migrate 官方 issue 中有过详细讨论与总结,建议团队制定明确的迁移命名与合入纪律(如约定合入前检查db/migrations目录的版本冲突)。
2. 尽量让迁移幂等
考虑让迁移具备幂等性——即同一段 SQL 连续执行两次应得到相同结果,这会让迁移更健壮。但幂等也有代价:它削弱了对数据库 schema 的控制。文档中的经典例子:
- 假设你忘记在 down 迁移中
DROP TABLE; - 执行 down 迁移后表仍然存在;
- 再次执行 up 迁移时,普通
CREATE TABLE会报错——这反而帮助你发现了 down 迁移中的问题; - 而如果用了
CREATE TABLE IF NOT EXISTS,则不会报错,问题被悄悄掩盖。
因此,"是否幂等"要权衡使用:在确保 down 迁移正确清理的前提下,再考虑用IF NOT EXISTS等幂等写法增强健壮性。
3. 多条命令请用事务包裹
如果一次迁移中包含多条命令/查询,应将其包裹在事务中(前提是你的数据库支持事务 DDL)。这样一旦其中某条命令失败,整个数据库保持原状,避免半途而废的脏状态。从源码角度看,每个数据库驱动都实现了Run等接口来执行迁移,事务行为由驱动各自处理,因此"是否支持事务 DDL"(例如 PostgreSQL 支持、而某些数据库不支持)取决于所选数据库。
运行迁移
创建并填充好迁移后,通过 CLI 或你的应用来执行迁移,并检查预期变更是否生效。CLI 的基本用法:
migrate -database YOUR_DATABASE_URL -path PATH_TO_YOUR_MIGRATIONS up-path是-source=file://path的简写(见 internal/cli/main.go 的翻译逻辑),两者等价。migrate 将迁移源(本地文件系统、io/fs、GitHub、GitLab、S3、GCS 等,见 README.md)与数据库驱动解耦:源码从 source 读取,按序应用到 database。
常用命令一览
migrate CLI 提供的完整命令集(源码见 internal/cli/main.go):
| 命令 | 作用 |
|---|---|
create ... | 创建一对 up/down 迁移文件 |
goto V | 迁移到指定版本 V |
up [N] | 应用全部(或 N 个)up 迁移 |
down [N] [-all] | 应用 N 个 down 迁移;-all应用全部(默认需要 y/N 确认) |
drop [-f] | 清空数据库全部内容(默认需要确认,-f跳过) |
force V | 直接设置版本 V 而不执行迁移(忽略 dirty 状态) |
version | 打印当前迁移版本 |
常用全局选项:-source、-path、-database、-prefetch N(预读迁移数,默认 10)、-lock-timeout N(获取数据库锁的超时秒数,默认 15)、-verbose、-version、-help。
例如只执行前两个迁移:
migrate -source file://path/to/migrations -database postgres://localhost:5432/database up 2若迁移托管在远程仓库,source 换成对应驱动即可,例如-source github://mattes:personal-access-token@mattes/migrate_test。CLI 收到 SIGINT(Ctrl+C)时会在安全断点优雅停止(通过Migrate.GracefulStop通道实现),若需立即终止可发送 SIGKILL。CLI 安装方式(预编译二进制、Homebrew、scoop、deb 包、go install)详见 cmd/migrate/README.md。
在应用代码中使用
migrate 同时是 Golang 库,只需把代码加进你的应用即可运行。最简单的用法(源码见 README.md):
import ( "github.com/golang-migrate/migrate/v4" _ "github.com/golang-migrate/migrate/v4/database/postgres" _ "github.com/golang-migrate/migrate/v4/source/file" ) func main() { m, err := migrate.New( "file:///migrations", "postgres://localhost:5432/database?sslmode=enable") if err != nil { // 处理错误 } m.Up() // 或 m.Steps(2) 显式指定执行条数 }底层Migrate对象(migrate.go)提供了Up()、Down()、Steps(n)、Migrate(version)、Force(version)、Version()、Drop()等方法,并支持PrefetchMigrations预读与LockTimeout加锁超时配置,还自带优雅停止与线程安全设计。若你已有数据库连接(*sql.DB),可改用migrate.NewWithDatabaseInstance配合WithInstance构造驱动实例。
提交前的验证流程
在提交迁移之前,官方建议执行完整的"往返"验证:
- 运行 up 迁移;
- 运行 down 迁移;
- 再次运行 up 迁移。
这能确认迁移双向都正常工作。例如:如果 up 迁移创建了表而对应的 down 迁移没有删除它,那么再次执行 up 时就会遇到错误,问题在提交前就会被发现。
同时建议在独立的容器化环境中验证迁移(例如结合 Docker 测试工具 dktest、dockertest 等,在隔离容器里跑真实的数据库实例做冒烟测试),避免污染本地开发环境。
多实例部署必须使用支持锁定的数据库
IMPORTANT:如果要在不同机器上运行应用的多个实例,务必使用支持迁移锁定的数据库,否则多个实例并发执行迁移可能引发问题。这一建议与源码设计一致:Migrate在执行前会调用lock()获取数据库锁,DefaultLockTimeout为 15 秒(超时返回ErrLockTimeout),并在执行结束后释放。以 PostgreSQL 驱动为例(database/postgres/postgres.go),驱动会维护一张schema_migrations表(version bigint not null primary key, dirty boolean not null),通过SetVersion记录版本与脏标记,从而为并发安全提供基础。
处理失败的迁移:force 命令与 dirty 状态
当某条迁移执行出错时,migrate 会阻止你在同一数据库上继续执行其他迁移。你会看到形如Dirty database version 1. Fix and force version的错误——这正是 migrate.go 中ErrDirty的原始文案,意味着数据库已被标记为dirty(脏)。
此时你需要:
- 调查迁移错误:判断这次失败的迁移是部分应用了,还是完全没应用;
- 用
force命令修正版本记录,使其反映数据库的真实状态:
migrate -path PATH_TO_YOUR_MIGRATIONS -database YOUR_DATABASE_URL force VERSION例如,若错误迁移完全没执行,就force回它之前的版本;若它已部分执行(如表已建好),则force到该版本本身。修复迁移内容后,再force到正确版本,数据库即恢复 clean,可以继续迁移。
force的底层实现是Migrate.Force(version)(migrate.go):它直接调用驱动的SetVersion(version, false)写入版本并把 dirty 标记重置为 false,不检查当前版本、不执行任何迁移(因此也能忽略 dirty 状态使用)。CLI 层面force V要求参数V >= -1(-1 表示无迁移的初始版本)。官方 issue 中也有针对 force 用法与示例的详细讨论,可作参考。
进一步阅读
- PostgreSQL 实战教程:以 PostgreSQL 为例的完整演练;
- 迁移最佳实践:文件名格式、内容格式、可逆性等深入规范;
- FAQ:常见问题解答(如为什么每个迁移要分 up/down 两个文件);
- CockroachDB 实战教程;
- 仓库源码:CLI 实现见 internal/cli/main.go 与 internal/cli/commands.go,核心库逻辑见 migrate.go 与 migration.go,文件名解析见 source/parse.go。
- 数据库
- 开发工具
- CLI
【免费下载链接】migrate
Database migrations. CLI and Golang library.
相关推荐
OptiScaler 错误排查指南:7 类高频故障按时间轴的修复方法
OptiScaler 错误排查指南:7 类高频故障按时间轴的修复方法 OptiScaler 出问题时,九成是配置问题,而不是工具本身。本文按"启动前 → 启动时
图形学游戏开发树莓派上的数据库迁移:使用golang-migrate/migrate管理Raspbian数据
树莓派上的数据库迁移:使用golang migrate/migrate管理Raspbian数据 在树莓派(Raspbian)上开发应用时,数据库结构的变更管理常
数据库开发工具CLIPyfolio完整指南:5个技巧掌握Python投资组合分析工具
Pyfolio完整指南:5个技巧掌握Python投资组合分析工具 Pyfolio是Python生态中专业的投资组合风险分析工具,为投资经理和量化分析师提供全面的
金融科技数据分析
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考