migrate cli是go生态数据库迁移的事实标准,必须用migrate create生成000001_init.up.sql/.down.sql成对文件,url需带sslmode=disable等参数并转义密码,驱动须显式导入,启动时应限连接池并避免复用实例。

migrate 命令行工具是 Go 生态中数据库迁移的事实标准,不是“可选方案”,而是避免 schema_migrations 表与实际结构脱节的唯一稳妥路径。自己用 database/sql 拼 SQL 执行顺序,90% 的线上 schema 不一致问题都源于此。
迁移文件命名和生成必须严格按规则
手写文件名或漏掉 .up.sql 后缀会导致 migrate up 静默跳过——它不报错,也不建表,只当没看见。
- 正确命名:必须是
000001_init.up.sql+000001_init.down.sql(序号不能跳、不能重复) - 推荐生成方式:
migrate create -ext sql -dir ./migrations -seq init,自动产出一对文件 -
.down.sql不可省略,哪怕内容为空或只写DROP TABLE IF EXISTS users;,否则migrate down直接失败 - 别用时间戳命名混搭序号模式(比如一边用
20240501_init.up.sql,一边又用000002_add_index.up.sql),排序逻辑会错乱
数据库 URL 必须带驱动参数且密码需转义
连接失败常不是网络问题,而是 URL 解析失败。PostgreSQL 必须加 ?sslmode=disable,MySQL 必须加 ?parseTime=true,否则 migrate 连不上就 panic。
- 错误示例:
postgres://user:pass@localhost/db→ 缺少sslmode,直接报unknown driver "postgres" - 正确写法:
postgres://user:pass@localhost/db?sslmode=disable - 密码含
@或/时,必须用url.QueryEscape处理,否则解析截断(如user:p@ss/word会被当成 host 是ss/word) - 驱动名大小写敏感:
postgres可以,Postgres或PG会报未知驱动
Go 代码中调用 migrate.Up 前要关事务、控连接池
在服务启动时嵌入迁移,最容易踩的是 PostgreSQL DDL 在事务里报错:ERROR: CREATE TABLE cannot be executed from a function。这不是 bug,是设计限制。
- 调用
migrate.New时,必须显式导入驱动:_ "github.com/golang-migrate/migrate/v4/database/postgres" -
migrate.Up()默认为每个 SQL 文件启一个事务——但 PostgreSQL 的CREATE TABLE等语句在事务块中不被允许(尤其在函数内调用时) - 解决方案:初始化时传
migrate.WithInstance(db),并确保db.SetMaxOpenConns(1),避免并发写schema_migrations表锁死 - 别复用同一个
*migrate.Migrate实例多次调用Up(),内部状态会缓存已执行版本,第二次返回no change
migrate down 失败后状态容易卡住
migrate down 不是“撤销上一条 SQL”,而是按序号倒着执行所有 .down.sql。一旦某条失败(比如删表前没清外键),已成功执行的 down 步骤仍会写入 schema_migrations 表,后续 up 就跳过修复。
- 典型错误现象:
migrate down 1报错后,再migrate version显示版本已降,但表结构没回退干净 - 修复方式:手动
DELETE FROM schema_migrations WHERE version = XXX;,再重试 - 上线前必做:
migrate -path ./migrations -database "xxx" validate检查 SQL 语法;version确认当前状态,别依赖记忆 - 容器环境尤其注意:挂载迁移目录时权限要够,
docker run --rm -v $(pwd):/migrations migrate/migrate容易因 UID 不匹配导致读不到文件
真正难的不是写迁移脚本,而是让 schema_migrations 表的状态始终和数据库结构对得上。每次 up 或 down 后,多看一眼 SELECT * FROM schema_migrations ORDER BY version DESC LIMIT 3;,比任何文档都管用。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











