最主流方案是golang-migrate/migrate库,CLI与SDK同源;运维/CI场景优先用CLI,应用启动绑定必须用SDK;DSN需严格匹配驱动前缀(如postgres://);迁移文件须含时间戳和.up/.down后缀;SDK中需校验版本号并开启日志。

用 migrate CLI 还是 golang-migrate/migrate 库?
Go 生态里最主流的迁移方案就是 golang-migrate/migrate(官方维护的 Go 版本),它既提供命令行工具 migrate,也提供可嵌入程序的 Go SDK。别被名字搞混:CLI 和库是同一套逻辑,只是调用方式不同。
你得选一种主用方式——不是“都用”,而是“谁主导”。
- 如果迁移脚本由运维或 CI 触发(比如部署前自动跑迁移),优先用
migrateCLI,配 shell 脚本或 Makefile 更稳 - 如果要和应用启动绑定(比如
main()里检查并执行迁移),必须用 SDK,调migrate.New()+.Up() - 别在代码里拼接
exec.Command("migrate", ...)—— 权限、路径、环境变量容易出错,SDK 已经帮你封装好了
migrate 支持哪些数据库驱动?怎么配 DSN?
migrate 不自己连数据库,靠第三方驱动(比如 github.com/lib/pq 或 github.com/go-sql-driver/mysql)提供底层能力。它只认 DSN 字符串格式,且严格区分驱动名前缀:
- PostgreSQL:DSN 必须以
postgres://开头,不能用postgresql://(后者会报unknown driver "postgresql") - MySQL:用
mysql://,注意用户名密码要 URL 编码(比如@得写成%40) - SQLite:用
sqlite3://,路径必须是绝对路径或以file:开头,相对路径会静默失败
常见错误:
- 把
host=localhost写成host=127.0.0.1导致权限拒绝(PostgreSQL pg_hba.conf 没配对) - DSN 里漏了
?sslmode=disable(本地开发时),结果卡在 TLS 握手 - 在 Windows 上用反斜杠路径:
sqlite3://.\db.sqlite→ 必须改成正斜杠或file:./db.sqlite
迁移文件命名和内容有什么硬性规则?
migrate 对文件名极其敏感,必须符合 ^\d+<em>[\w</em>]+.up.sql$ 或 ^\d+<em>[\w</em>]+.down.sql$ 格式,比如:
- ✅
1598765432_add_users_table.up.sql - ❌
add_users_table.up.sql(缺时间戳前缀) - ❌
1598765432_add_users_table.sql(缺.up或.down后缀)
SQL 文件里:
立即学习“go语言免费学习笔记(深入)”;
- 只能写纯 SQL,不支持 Go 模板、变量插值或注释块(
/<em> </em>/可以,但--注释后不能换行) - 每个
.up.sql必须有对应.down.sql,否则migrate down会报错(即使你从不用 down) - PostgreSQL 用户注意:DDL 语句如
CREATE TABLE不能和事务混用(除非显式加BEGIN; ... COMMIT;),否则迁移会中断
Go SDK 中 migrate.Up() 容易卡住或静默失败?
migrate.Up() 默认行为是“迁移到最新版本”,但它不会主动报错退出——比如网络断开、锁表、语法错误,可能只返回 nil 错误但实际没执行。
必须检查两个东西:
- 返回的
err:不是所有失败都进 err,有些只是日志输出 - 执行后的当前版本号:
m.Source.Version(),对比预期目标版本,不一致就得查日志 - 加
log.SetOutput(os.Stderr)才能看到迁移过程中的 SQL 和警告(默认不输出)
更稳妥的做法:
- 用
migrate.Steps(1)代替migrate.Up(),一次只跑一个版本,便于定位哪步挂了 - 在
Up()前先调m.Up(0)(即检查连接和 migration 表是否存在),提前暴露配置问题 - 不要在生产环境自动
Up(),至少加个--dry-run类似的开关(SDK 没原生支持,得自己比对m.Plan()结果)
迁移不是“写完 SQL 就完事”,版本号冲突、跨环境 DSN 差异、down 脚本缺失、驱动加载顺序,任何一个点松动都会让上线卡在凌晨三点。










