Go语言生产环境禁用GORM AutoMigrate,因其无版本记录、不支持回滚、非幂等且无法删改字段;必须使用migrate CLI配合严格命名的.up/.down文件,并手动验证DDL事务与逆向逻辑。

Go 语言没有内置数据库迁移能力,必须用第三方工具;生产环境只认 migrate CLI,别碰 AutoMigrate。
为什么不能用 GORM 的 AutoMigrate 上线
AutoMigrate 不是迁移工具,它只是结构同步快照:不记录版本、不生成回滚脚本、不保证幂等。上线后等于裸奔。
- 删了 struct 字段(比如
Age int),表里字段还在,后续SELECT *直接 panic - 多个实例同时启动,可能并发执行,触发 PostgreSQL 的
relation already exists错误 - 它只增不减 —— 加字段、建索引可以,删字段、改类型、重命名列全没反应
- 真要用,至少包一层检查:
if !db.Migrator().HasTable(&User{}) { db.AutoMigrate(&User{}) }
migrate CLI 迁移文件怎么命名和组织
迁移文件名决定执行顺序,必须严格按时间戳格式,且 .up.sql 和 .down.sql 成对存在。
- 正确命名:
20240520103000_add_users_table.up.sql和20240520103000_add_users_table.down.sql - 错误命名:
001_create_table.sql(无 .up/.down 后缀)、add_user.up.sql(缺时间戳)、只有 .up 没有 .down - SQLite 路径必须写绝对路径,否则
migrate在不同工作目录下找不到.db文件 - CLI 会在库中自动建
migrations表记录已执行版本,别手动删 —— 否则下次up会重复执行
生产环境跑迁移前必须验证的三件事
DDL 事务支持因数据库而异,migrate 默认也不开启事务执行单个 SQL 文件。靠工具自动兜底不现实,得人来卡住关键点。
立即学习“go语言免费学习笔记(深入)”;
- 先在同版本数据库上手动执行:
psql -f 20240520103000_add_users_table.up.sql,尤其注意 MySQL 的ONLY_FULL_GROUP_BY模式是否报错 - 涉及重命名、拆字段、类型转换的操作,
.up.sql里别写单条ALTER TABLE RENAME COLUMN,改用“临时表 + 数据迁移 + 重命名”三步走 - 如需强一致性,得在 SQL 文件开头加
BEGIN;,结尾加COMMIT;或ROLLBACK;——migrate不校验语法,写错就炸
Go 应用里要不要嵌入迁移逻辑
能不用就不用。CI/CD 或运维手动跑 migrate up 更可控;非要在代码里触发(比如 FaaS 场景),必须加锁、幂等、超时三重防护。
- 别直接调
migrate.Up(),先用migrate.NewWithDatabaseInstance()接入已初始化的*sql.DB,避免重复开连接 - 执行前必须调
m.Version()判断是否已是最新版,否则每次启动都扫迁移文件、浪费资源还可能出错 - PostgreSQL 用户要确保数据库用户有
CREATE TABLE权限;MySQL 要调大innodb_lock_wait_timeout,否则长迁移卡住会导致应用启动失败 - 必须加超时:
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second),失败就log.Fatal,不静默跳过
真正难的不是写命令或建文件,而是设计 .down.sql —— 它必须能完全逆向 .up.sql 的副作用。比如 .up.sql 插了初始化数据,.down.sql 就得删对应行;SQL 里不能依赖 Go 变量,也不能写条件判断,这些全得人想清楚,工具帮不上忙。


















