gorm automigrate 默认仅增量补全表结构,不删字段、不改类型、不校验约束一致性,故不可用于生产环境;应结合golang-migrate等版本化工具实现可控迁移。

gorm 自动生成表结构时,默认行为是“仅创建不存在的表”,不会修改已有字段或索引。这看似安全,实则埋下隐患:开发环境改了 struct 字段类型或加了 gorm:"not null",但线上表结构不动,运行时可能 panic 或静默丢数据。
gorm.AutoMigrate 为什么不能直接用于生产环境
它只做“增量补全”,不校验一致性:
- 字段类型变更(如
string→*string)不会触发 ALTER COLUMN,旧数据可能被截断或报错 - 删除 struct 字段后,对应数据库列仍存在,变成“幽灵字段”
- 添加
gorm:"uniqueIndex"不会自动建索引,除非该索引名此前完全未出现过 - 对已存在的表,
AutoMigrate不检查DEFAULT、CHECK约束是否匹配
struct 标签写法直接影响迁移可靠性
常见错误是依赖默认映射,忽略显式约束声明:
-
gorm:"primaryKey"比字段名叫ID更可靠——避免因命名习惯差异导致主键遗漏 -
gorm:"type:decimal(10,2);not null"明确指定精度和非空,比仅靠 Go 类型推断更可控 -
gorm:"default:CURRENT_TIMESTAMP"必须配type:datetime,否则 MySQL 可能报错或存为0000-00-00 - 用
gorm:"column:created_at"统一列名风格,避免大小写混用在不同数据库(如 PostgreSQL 对大小写敏感)
上线前必须人工核对的三件事
无论是否用 AutoMigrate,发布前必须确认:
- 执行
db.Migrator().FullTableCheck() == true(GORM v1.25+),它会对比 struct 和表结构并返回不一致项,但不自动修复 - 导出当前表结构 SQL(
SHOW CREATE TABLE users),与本地AutoMigrate生成的 DDL 做文本 diff - 检查外键:GORM 默认不创建外键约束,需显式加
gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"
推荐替代方案:分阶段迁移 + 版本化 SQL
真正可控的方式不是依赖 ORM 自动推导:
- 开发阶段用
AutoMigrate快速验证,但禁用在 prod 初始化流程中 - 每个版本维护独立 migration 文件(如
migrations/20260601_add_user_balance.sql),含 UP / DOWN 语句 - 用
golang-migrate/migrate或pressly/goose执行,支持回滚、锁表控制、dry-run 预检 - struct 只作为运行时模型,不承担 schema 定义职责;DDL 和代码保持单向同步(SQL → struct)
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











