同一个字段反复建索引?GORM MySQL 1069 错误的罪魁祸首
前言
某天重启服务,日志里蹦出这么一行错误:
Error 1069 (42000): Too many keys specified; max 64 keys allowed
ALTER TABLE `xxx_table` MODIFY COLUMN `some_field` varchar(64) UNIQUE ...
打开数据库一看,单个表的索引数量已经快接近 MySQL InnoDB 的 64 个 硬上限。更离谱的是,同一个字段上竟然挂着好几个功能一模一样的唯一索引,名字却各不相同:
idx_table_some_fieldsome_fieldsome_field_2some_field_3
每次服务重启或重新部署,这些“幽灵索引”就可能再多冒出来一个,直到把上限撑爆,服务直接起不来。
罪魁祸首:偷懒的 GORM 标签
问题出在模型定义里:
// 有问题的写法
SomeField string `gorm:"column:some_field;type:varchar(64);uniqueIndex;comment:xxx"`
关键在于 uniqueIndex 后面没有写索引名称。
GORM 在 AutoMigrate 时,如果索引标签不指定名字,就会自己动态生成一个默认名称。这个生成规则不稳定,不同版本、不同执行顺序、甚至表名长度变化,都可能生成不同的名字。
于是就出现了下面这个循环:
- GORM 发现“当前索引和我想要的不一样” → 创建一个新索引
- 旧索引不删除(AutoMigrate 只增不删)
- 下次再跑,又生成一个新名字的索引……
- 循环往复,索引越来越多
正确写法:给每个索引一个固定“身份证”
解决方法非常简单——永远给索引指定明确的名称:
// 推荐写法
SomeField string `gorm:"column:some_field;type:varchar(64);uniqueIndex:uk_table_some_field;comment:xxx"`
加上 :uk_table_some_field 之后,GORM 的行为就变得稳定且幂等了:
- 索引已经存在 → 什么都不做
- 索引不存在 → 按指定名称创建
- 无论跑多少次 AutoMigrate,结果都一样
普通索引同理:
// 普通索引也必须带名字
SomeField string `gorm:"index:idx_table_some_field"`
已经产生的冗余索引怎么清理?
改完代码只能防止继续出血,数据库里已经堆积的垃圾索引还需要手动清理:
-- 1. 查看某个字段上的所有索引
SHOW INDEX FROM your_table_name WHERE Column_name = 'some_field';
-- 2. 删除重复的索引(根据上面查到的名字删)
ALTER TABLE `your_table_name`
DROP INDEX `idx_xxx`,
DROP INDEX `some_field`,
DROP INDEX `some_field_2`;
-- 3. 重新添加固定名称的唯一索引
ALTER TABLE `your_table_name`
ADD UNIQUE INDEX `uk_table_some_field` (`some_field`);
清理完成后重启服务,AutoMigrate 就不会再乱创建新索引了。
团队开发规范建议
为了避免这个问题反复出现,建议把以下规则加到团队规范里:
- 禁止无名索引:所有
index/uniqueIndex必须带名称 - 统一命名规范:
- 普通索引:
idx_表名_字段名 - 唯一索引:
uk_表名_字段名
- 普通索引:
- CI 流水线拦截:加个简单脚本,禁止提交无名索引的代码
- 定期巡检:重点表定期执行
SHOW INDEX,关注索引数量

浙公网安备 33010602011771号