MySQL 中 KEY 的使用
在 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`)
它有两个作用:
- 提高查询效率;
- 限制 org_code 不能重复。
比如表里已经有:
W3205082026040901
如果再次插入相同的 org_code,数据库会直接报错。
这类索引特别适合业务编号,比如:
org_code config_no order_no phone email
如果业务上要求“全局唯一”,就应该用 UNIQUE KEY,不能只用普通 KEY。
3. PRIMARY KEY 是什么?
PRIMARY KEY 是主键。
例如:
PRIMARY KEY (`id`)
主键有几个特点:
- 一张表只能有一个主键;
- 主键值不能重复;
- 主键字段不能为 NULL;
- 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. 为什么重复索引不好?
重复索引不是“多多益善”。
它会带来成本:
-
占磁盘空间
- 每个索引都要单独存储。
-
降低写入性能
- INSERT、UPDATE、DELETE 时,数据库不只要改数据,还要维护索引。
-
增加优化器判断成本
- 索引太多时,优化器要在更多候选索引里选择。
-
维护成本变高
- 表结构越来越乱,后面的人看着会挠头。
所以索引不是贴纸,不是觉得重要就给字段贴三层。
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`)
那就是纯重复,没啥悬念。
更多推荐




所有评论(0)