gorm中定义多列唯一索引需字段共用相同索引名(如index:idx_first_second,unique),顺序由结构体声明顺序决定;单字段用uniqueindex,复合索引须手动调用createindex控制细节。

使用 GORM 的 UniqueIndex 方法创建唯一索引
直接在模型结构体上用 GORM 标签声明唯一索引最常用,但要注意:它只在 AutoMigrate 时生效,且不支持复合唯一索引的字段顺序控制。
示例:
type User struct {
ID uint `gorm:"primaryKey"`
Email string `gorm:"uniqueIndex"`
Username string `gorm:"uniqueIndex"`
}
-
uniqueIndex标签会为单个字段生成名为idx_users_email这样的索引名(前缀是表名) - 若两个字段共用一个索引名(如都写
gorm:"uniqueIndex:idx_email_or_name"),GORM 会合并成复合唯一索引 - ⚠️ 坑:字段类型为
string且未指定长度时,MySQL 可能报错Specified key was too long,需加size限制,比如gorm:"uniqueIndex;size:255"
用 db.Migrator().CreateIndex() 手动建复合唯一索引
当需要精确控制字段顺序、索引名或条件表达式时,必须绕过标签,调用迁移器接口。
示例(MySQL/PostgreSQL):
db.Migrator().CreateIndex(&User{}, "idx_users_email_verified_at")
// 或按字段名显式指定
db.Migrator().CreateIndex(&User{}, "email,verified_at")
- 第二个参数是索引名;若传入逗号分隔的字段名字符串(如
"email,verified_at"),GORM 会自动创建复合唯一索引 - PostgreSQL 支持表达式索引,例如
db.Migrator().CreateIndex(&User{}, "LOWER(email)"),但 MySQL 不支持该写法 - ⚠️ 坑:如果表已存在且数据违反唯一约束,
CreateIndex会失败并抛出数据库错误,需先清理脏数据
删除或重建唯一索引时的注意事项
GORM 不会自动删除旧索引,AutoMigrate 仅新增或修改字段,不删索引。手动管理索引生命周期必须主动操作。
- 删索引用
db.Migrator().DropIndex(&User{}, "idx_name"),索引名必须和创建时完全一致 - 若用
uniqueIndex标签改名(比如从uniqueIndex:idx_old改成uniqueIndex:idx_new),旧索引不会被自动清理 - SQLite 不支持
DROP INDEX IF EXISTS,所以DropIndex在 SQLite 上失败会 panic,建议加db.Migrator().HasIndex(&User{}, "idx_name")预检
验证唯一索引是否生效的简单方法
别只信日志或文档,直接用数据库客户端查系统表,或写一行测试代码触发冲突。
- MySQL 查索引:
SHOW INDEX FROM users WHERE Key_name = 'idx_users_email'; - PostgreSQL 查索引:
\d users或SELECT indexname FROM pg_indexes WHERE tablename = 'users'; - 代码验证:插入两条相同
Email的User,第二次db.Create()应返回类似ERROR: duplicate key value violates unique constraint "idx_users_email"的错误(具体信息依驱动而异) - ⚠️ 坑:GORM 默认开启事务,但某些场景(如用
Save更新主键后重复插入)可能因事务隔离级别掩盖问题,建议在测试中显式用db.Session(&gorm.Session{AllowGlobalUpdate: true})控制上下文
唯一索引的字段顺序、NULL 处理、大小写敏感性这些细节,全由底层数据库决定,GORM 只负责发指令——写完记得去数据库里确认实际建出来的索引长什么样。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











