在 MySQL 建表语句里,经常能看到这些写法:

PRIMARY KEY (`id`),

KEY `idx_org_code` (`org_code`),

UNIQUE KEY `uk_org_code` (`org_code`)

很多人第一次看会疑惑:KEY、INDEX、UNIQUE KEY 是不是一回事?它们到底有什么区别?

一句话总结:

KEY 基本等同于普通索引,用来加快查询;UNIQUE KEY 是唯一索引,既能加快查询,也能防止重复数据。


1. KEY 和 INDEX 是一样的吗?

在 MySQL 里,下面两种写法基本等价:

KEY `idx_org_code` (`org_code`)

INDEX `idx_org_code` (`org_code`)

它们都是普通索引

普通索引的主要作用是提高查询效率,比如:

SELECT * FROM t_org WHERE org_code = 'W3205082026040901';

如果 org_code 上有索引,数据库就可以更快地找到这条数据。

但是普通索引有一个重点:

普通索引不保证数据唯一。

也就是说,即使你建了这个索引:

KEY `idx_org_code` (`org_code`)

数据库依然允许插入重复的 org_code。


2. UNIQUE KEY 是什么?

UNIQUE KEY 是唯一索引。

例如:

UNIQUE KEY `uk_org_code` (`org_code`)

它有两个作用:

  1. 提高查询效率;
  2. 限制 org_code 不能重复。

比如表里已经有:

W3205082026040901

如果再次插入相同的 org_code,数据库会直接报错。

这类索引特别适合业务编号,比如:

org_code config_no order_no phone email

如果业务上要求“全局唯一”,就应该用 UNIQUE KEY,不能只用普通 KEY。


3. PRIMARY KEY 是什么?

PRIMARY KEY 是主键。

例如:

PRIMARY KEY (`id`)

主键有几个特点:

  1. 一张表只能有一个主键;
  2. 主键值不能重复;
  3. 主键字段不能为 NULL;
  4. InnoDB 表的数据会按照主键组织存储。

最常见的写法是:

id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', PRIMARY KEY (`id`)

主键通常用于数据库内部定位一行数据。

业务编号虽然也唯一,但一般不建议直接当主键,通常还是用 id 做主键,业务编号再加唯一索引。


4. 常见 KEY 类型对比

类型 示例 是否唯一 是否允许 NULL 主要作用
PRIMARY KEY PRIMARY KEY (id) 主键定位数据
UNIQUE KEY UNIQUE KEY uk_code (code) 通常允许 防重复 + 加速查询
KEY / INDEX KEY idx_name (name) 允许 加速查询
FULLTEXT KEY FULLTEXT KEY ft_title (title) 允许 全文搜索
SPATIAL KEY SPATIAL KEY idx_geo (location) 允许 空间/地理数据查询

5. 一个字段能不能同时有多种 KEY?

可以。

比如理论上可以写:

PRIMARY KEY (`id`), KEY `idx_id` (`id`)

也可以写:

UNIQUE KEY `uk_org_code` (`org_code`), KEY `idx_org_code` (`org_code`)

数据库并不是完全不允许。

但问题是:很多时候没必要。

因为索引之间存在“能力包含关系”。


6. KEY 之间有哪些包含关系?

6.1 PRIMARY KEY 包含唯一能力和索引能力

PRIMARY KEY (`id`)

它已经具备:

  • 唯一约束;
  • 非空约束;
  • 索引查询能力。

所以一般不用再给 id 建:

UNIQUE KEY `uk_id` (`id`) KEY `idx_id` (`id`)

这些都属于重复索引。

这就像身份证已经能证明你是你,就没必要再拿一张写着“我确实是我”的便利贴。


6.2 UNIQUE KEY 包含普通索引能力

UNIQUE KEY `uk_org_code` (`org_code`)

它既能防重复,也能加速查询。

所以如果已经有:

UNIQUE KEY `uk_org_code` (`org_code`)

通常不需要再建:

KEY `idx_org_code` (`org_code`)

因为唯一索引已经能支持:

WHERE org_code = ?

普通索引并不会让这个查询再快出什么明显效果,反而会增加维护成本。


6.3 普通 KEY 不包含唯一能力

KEY `idx_org_code` (`org_code`)

它只能加速查询,不能防重复。

如果业务要求 org_code 唯一,就应该升级成:

UNIQUE KEY `uk_org_code` (`org_code`)

而不是想着普通索引能顺手帮你防重。它没这个超能力。


7. 为什么重复索引不好?

重复索引不是“多多益善”。

它会带来成本:

  1. 占磁盘空间

    • 每个索引都要单独存储。
  2. 降低写入性能

    • INSERT、UPDATE、DELETE 时,数据库不只要改数据,还要维护索引。
  3. 增加优化器判断成本

    • 索引太多时,优化器要在更多候选索引里选择。
  4. 维护成本变高

    • 表结构越来越乱,后面的人看着会挠头。

所以索引不是贴纸,不是觉得重要就给字段贴三层。


8. 什么情况下一个字段会出现在多个索引里?

虽然重复索引不推荐,但一个字段出现在多个索引里不一定错。

关键看这些索引是不是服务不同查询场景。

8.1 合理情况:不同联合索引服务不同查询

例如:

KEY `idx_region_status` (`province`, `city`, `district`, `status`), KEY `idx_region_create_time` (`province`, `city`, `district`, `create_time`)

这两个索引都包含:

province, city, district

但它们服务的查询不同:

WHERE province = ? AND city = ? AND district = ? AND status = ?

和:

WHERE province = ? AND city = ? AND district = ? ORDER BY create_time DESC

这种情况下可能都有意义。


8.2 可能多余的情况:唯一字段再加联合索引

例如:

UNIQUE KEY `uk_org_code` (`org_code`), KEY `idx_org_code_status` (`org_code`, `status`)

如果 org_code 已经全局唯一,那么:

WHERE org_code = ? AND status = ?

其实通过 uk_org_code 已经最多定位到一条数据了,status 再过滤一下就行。

这种情况下,idx_org_code_status 很可能是多余的。


9. 联合索引里的包含关系:最左前缀

联合索引有一个很重要的规则:最左前缀原则

例如:

KEY `idx_region_status` (`province`, `city`, `district`, `status`)

这个索引通常可以支持:

WHERE province = ?

也可以支持:

WHERE province = ? AND city = ?

也可以支持:

WHERE province = ? AND city = ? AND district = ?

还可以支持:

WHERE province = ? AND city = ? AND district = ? AND status = ?

因为这些查询都从索引最左边的字段开始匹配。

所以如果已经有:

KEY `idx_region_status` (`province`, `city`, `district`, `status`)

通常没必要再建:

KEY `idx_province` (`province`) KEY `idx_province_city` (`province`, `city`)

它们大概率是重复索引。

但是这个联合索引通常不能很好支持:

WHERE city = ?

因为 city 不是最左字段。

也就是说,联合索引像排队进场,得从队头开始,不能直接从中间插队。


10. 怎么判断一个索引有没有必要?

可以按这个顺序判断:

10.1 这个字段是否需要防重复?

如果需要防重复:

UNIQUE KEY

如果不需要防重复:

KEY / INDEX

例如业务编号:

UNIQUE KEY `uk_org_code` (`org_code`)

普通查询字段:

KEY `idx_status` (`status`)


10.2 已有更强索引是否覆盖?

如果已经有:

PRIMARY KEY (`id`)

就不要再建:

KEY `idx_id` (`id`)

如果已经有:

UNIQUE KEY `uk_org_code` (`org_code`)

通常就不要再建:

KEY `idx_org_code` (`org_code`)


10.3 联合索引是否已经覆盖最左前缀?

如果已经有:

KEY `idx_a_b_c` (`a`, `b`, `c`)

通常不需要再建:

KEY `idx_a` (`a`) KEY `idx_a_b` (`a`, `b`)

但它不覆盖:

KEY `idx_b` (`b`) KEY `idx_c` (`c`)


10.4 查询场景是否真的不同?

不同过滤条件、排序条件、范围查询,可能需要不同联合索引。

比如:

KEY `idx_user_status_time` (`user_id`, `status`, `create_time`), KEY `idx_user_type_time` (`user_id`, `type`, `create_time`)

如果业务确实经常分别按 status 和 type 查,这两个索引可能都有价值。

但如果只是:

KEY `idx_user_id` (`user_id`), KEY `idx_user_id_copy` (`user_id`)

那就是纯重复,没啥悬念。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐