GORM v2中单字段唯一索引用gorm:"unique",多字段联合唯一需共用相同索引名并加unique,如index:idx_a_b,unique;AutoMigrate只增不删索引,旧索引须手动清理。

GORM v2 中添加唯一索引,关键看你是要单字段唯一,还是多字段联合唯一——两者写法完全不同,混用会直接导致数据库约束失效。
单字段唯一索引怎么写
最常见也最容易写错:用 unique 标签即可,但必须写在字段 tag 里,且不能和 index 混搭成 index,unique(GORM 不识别这种组合)。
-
gorm:"unique"→ 创建唯一约束 + 唯一索引(MySQL/PostgreSQL 都生效) -
gorm:"uniqueIndex"→ 仅创建唯一索引,不加 CONSTRAINT(部分场景可用,但数据完整性弱于unique) - 错误示例:
gorm:"index;unique"或gorm:"sql:unique"—— 前者被忽略,后者是 v1 语法,v2 已废弃
示例:
type User struct {
gorm.Model
Email string `gorm:"unique"`
Username string `gorm:"uniqueIndex"`
}
执行 db.AutoMigrate(&User{}) 后,Email 字段会有 UNIQUE CONSTRAINT + INDEX,Username 只有 INDEX(无约束),插入重复值时前者报错,后者可能静默失败(取决于驱动行为)。
多列联合唯一索引怎么写
不能给每个字段单独加 unique,否则生成的是两个独立单列唯一索引,不是你想要的「(A,B) 组合唯一」。
- 所有参与字段必须使用相同的索引名,且都带
unique,例如gorm:"index:idx_a_b,unique" - 索引列顺序由结构体中字段声明顺序决定,
A在前、B在后 → 索引为(a,b),不可颠倒 - 索引名建议语义化,比如
idx_user_provider_id,避免用idx1这类无意义名称
正确示例:
type Identity struct {
gorm.Model
UserID uint `gorm:"index:idx_user_provider,unique"`
Provider string `gorm:"index:idx_user_provider,unique"`
ProviderID string `gorm:"not null"`
}
迁移后,数据库会建一个覆盖 user_id 和 provider 的联合唯一索引,允许同一 UserID 对应多个 Provider,但不允许同一 (UserID, Provider) 出现两次。
AutoMigrate 会不会自动删旧索引
不会。db.AutoMigrate() 只增不减:它会创建缺失的索引,但绝不会删除已有索引或修改索引定义。
- 如果你改了结构体 tag(比如把
idx_old改成idx_new),旧索引仍留在库中,新索引也会被新建 - 想清理旧索引,得手动调用
db.Migrator().DropIndex(&Model{}, "idx_old"),再跑一次AutoMigrate - 生产环境务必先在测试库验证索引变更,避免线上残留冗余索引拖慢写入
真正容易被忽略的是索引名一致性——哪怕只差一个下划线或大小写,GORM 就当它是两个不同索引,结果就是建出一堆单列唯一索引,业务侧却以为组合约束生效了。上线前用 SHOW CREATE TABLE 或 \d table_name 直接查数据库,比看代码更可靠。


















