同一个字段反复建索引?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_field
  • some_field
  • some_field_2
  • some_field_3

每次服务重启或重新部署,这些“幽灵索引”就可能再多冒出来一个,直到把上限撑爆,服务直接起不来。

罪魁祸首:偷懒的 GORM 标签

问题出在模型定义里:

// 有问题的写法
SomeField string `gorm:"column:some_field;type:varchar(64);uniqueIndex;comment:xxx"`

关键在于 uniqueIndex 后面没有写索引名称

GORM 在 AutoMigrate 时,如果索引标签不指定名字,就会自己动态生成一个默认名称。这个生成规则不稳定,不同版本、不同执行顺序、甚至表名长度变化,都可能生成不同的名字。

于是就出现了下面这个循环:

  1. GORM 发现“当前索引和我想要的不一样” → 创建一个新索引
  2. 旧索引不删除(AutoMigrate 只增不删)
  3. 下次再跑,又生成一个新名字的索引……
  4. 循环往复,索引越来越多

正确写法:给每个索引一个固定“身份证”

解决方法非常简单——永远给索引指定明确的名称

// 推荐写法
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 就不会再乱创建新索引了。

团队开发规范建议

为了避免这个问题反复出现,建议把以下规则加到团队规范里:

  1. 禁止无名索引:所有 index / uniqueIndex 必须带名称
  2. 统一命名规范
    • 普通索引:idx_表名_字段名
    • 唯一索引:uk_表名_字段名
  3. CI 流水线拦截:加个简单脚本,禁止提交无名索引的代码
  4. 定期巡检:重点表定期执行 SHOW INDEX,关注索引数量
posted @ 2026-07-14 14:45  牛奔  阅读(7)  评论(0)    收藏  举报