真正靠谱的路径只有一条:用migrate cli管理迁移文件,go应用只负责连库、不参与执行逻辑;因其纯sql驱动、严格命名、独立cli执行,可避免schema_migrations表与实际结构脱节,且内置锁机制保障并发安全。

别在 Go 框架启动时调 AutoMigrate,也别自己拼 db.Exec 执行 SQL 文件——这俩做法在线上环境踩坑率超 90%。真正靠谱的路径只有一条:用 migrate CLI 管理迁移文件,Go 应用只负责连库、不参与执行逻辑。
为什么不能在代码里直接调 migrate.Up()
看似方便,实则埋雷:
-
migrate.Up()默认为每个迁移文件启一个事务,而 PostgreSQL 的CREATE TABLE、MySQL 的ALTER TABLE ... DROP COLUMN在事务块里会直接报错(如ERROR: CREATE TABLE cannot be executed from a function) - 多个服务实例并发启动时,
schema_migrations表可能被多线程竞争写入,版本记录错乱 - 没设
db.SetMaxOpenConns(1),连接池默认开多连接,迁移语句实际是并行执行的,顺序无法保证 - 错误静默:某次
up失败但schema_migrations已写入部分记录 → 下次启动认为“已完成”,跳过关键步骤
迁移文件命名和内容必须严格合规
名字不对,migrate 就当它不存在;.down.sql 缺失或为空,down 命令直接失败。
- 必须用
migrate create -seq或-ts生成文件,禁止手写序号(如000001_init.up.sql)或混用时间戳+序号(如20240501_init.up.sql和000002_add_index.up.sql同目录) -
.up.sql和.down.sql必须成对存在,哪怕.down.sql只写DROP TABLE IF EXISTS users; - PostgreSQL 迁移里别写
IF NOT EXISTS——CREATE TABLE IF NOT EXISTS在down时无法逆向,且破坏幂等性 - MySQL 迁移必须加
?parseTime=true,否则TIMESTAMP字段解析失败
生产上线前必须跑的两个验证命令
这两个命令不改数据,但能提前暴露 80% 的部署事故。
-
migrate validate:检查所有.up.sql/.down.sql是否语法合法、是否成对、编号是否冲突(比如重复的000001_) -
migrate -database "" -path "./migrations" version:确认当前迁移目录最高序号,避免本地漏提文件;再配合migrate -database "your-url" version查线上 DB 当前版本,比对是否一致 - CI/CD 流水线里必须把这两步放进 pre-deploy 阶段,跳过等于裸奔
数据库 URL 容易被忽略的转义细节
连接失败十有八九不是网络问题,而是 URL 解析失败。
- PostgreSQL 密码含
@或/必须转义:p@ss/word→p%40ss%2Fword,否则解析器把p@ss/word当成 host - URL 必须带
?sslmode=disable(PostgreSQL)或?parseTime=true&loc=Local(MySQL),驱动名大小写敏感:postgres可以,Postgres报unknown driver - SQLite 路径不能用相对路径:
file://./migrations会失败,得用绝对路径file:///full/path/to/migrations
真正的难点不在怎么跑通命令,而在 down 脚本是否真能回滚——比如 MySQL 8.0+ 对 DROP COLUMN 加了元数据锁,线上执行可能阻塞查询;PostgreSQL 的 ALTER TABLE ... RENAME COLUMN 在 down 里没法无损还原。这些必须在预发环境实测,不能只看语法对不对。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











