InnoDB 中索引类型有哪些?
结论:按数据结构分有四类——B+Tree、全文(倒排)、空间(R-Tree)、自适应哈希;按业务用途分有主键、唯一、普通、联合、前缀、函数、降序索引。面试里说清「结构维度」与「用途维度」两条线,再补 8.0 的不可见索引与索引跳跃扫描,就比较完整。
一、两条分类线
按数据结构 按用途
├── B+Tree(默认,绝大多数) ├── 主键索引(聚簇)
├── 全文索引 FULLTEXT(倒排) ├── 唯一索引
├── 空间索引 SPATIAL(R-Tree) ├── 普通索引(二级)
└── 自适应哈希 AHI(自动,非显式) ├── 联合索引
├── 前缀索引
├── 函数索引(8.0.13+)
└── 降序索引(8.0+)
二、按数据结构
| 类型 | 实现 | 适用 | 限制 |
|---|---|---|---|
| B+Tree | 多路平衡树,叶子有序链表 | 等值、范围、排序、前缀匹配 | 前导模糊 %x 不可用 |
| 全文 FULLTEXT | 倒排索引 + 分词 | 大段文本关键词检索 | 需配置分词器,中文效果有限 |
| 空间 SPATIAL | R-Tree | 经纬度、几何包含判断 | 列必须 NOT NULL,仅 MyISAM/InnoDB 支持 |
| 自适应哈希 AHI | 缓冲池页的哈希表 | 热点等值查询 | 由 innodb_adaptive_hash_index 自动管理,不可手动建 |
三、按用途:建法与要点
主键索引:一张表一个,叶子存整行。
CREATE TABLE t_order (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id)
) ENGINE=InnoDB;
唯一索引、普通索引、联合索引:
ALTER TABLE t_order
ADD UNIQUE KEY uk_order_no (order_no), -- 唯一
ADD KEY idx_user_status (user_id, status), -- 联合,注意列顺序
ADD KEY idx_ctime (ctime); -- 普通
前缀索引:长字符串只索引前 N 个字符,省空间。
ALTER TABLE t_article ADD KEY idx_title (title(20));
-- 选 N:比较前缀区分度与整列区分度
SELECT
COUNT(DISTINCT LEFT(title, 10)) / COUNT(*) AS sel_10,
COUNT(DISTINCT LEFT(title, 20)) / COUNT(*) AS sel_20,
COUNT(DISTINCT title) / COUNT(*) AS sel_full
FROM t_article;
前缀索引无法用于覆盖索引,也无法支持 ORDER BY title,这是它的硬伤。
函数索引(8.0.13+,本质是隐藏的生成列 + 索引):
ALTER TABLE t_user
ADD KEY idx_phone_tail ((RIGHT(phone, 4)));
-- 查询必须写成与索引表达式一致的形式才能命中
SELECT id FROM t_user WHERE RIGHT(phone, 4) = '8888';
降序索引(8.0+,真正按 DESC 存储,而不是反向扫描):
ALTER TABLE t_order ADD KEY idx_user_ctime (user_id, ctime DESC);
SELECT * FROM t_order
WHERE user_id = 42
ORDER BY ctime DESC
LIMIT 20;
不可见索引(8.0+,用于安全下线索引):
ALTER TABLE t_order ALTER INDEX idx_ctime INVISIBLE;
-- 观察业务无异常后再真正删除
ALTER TABLE t_order DROP INDEX idx_ctime;
四、索引跳跃扫描(Skip Scan)
8.0.13 引入:联合索引 (a, b) 在查询只带 b 时,若 a 的取值很少,优化器可以逐个枚举 a 的值做多次等值探测,等价于「跳过最左列」。
ALTER TABLE t_order ADD KEY idx_status_user (status, user_id);
-- status 只有 5 种取值时可能触发 skip scan
EXPLAIN SELECT * FROM t_order WHERE user_id = 42;
-- Extra: Using index for skip scan
它是优化器的补救手段,不是设计依据:能按最左前缀建索引就不要指望它。
常见追问
- 自适应哈希索引需要手动建吗? 不需要,InnoDB 自动为热点页建哈希,
SHOW ENGINE INNODB STATUS的INSERT BUFFER AND ADAPTIVE HASH INDEX段能看到命中率。 - 全文索引能替代 Elasticsearch 吗? 小数据量可以,中文分词、相关性排序、聚合分析都不如 ES,且全文索引与事务、复制的配合较弱。
- 一张表能建多少个索引? 上限 64 个,但实际超过 5 个就要审视写入放大了。
- 函数索引和「在列上用函数」有什么区别? 后者让普通索引失效;函数索引把表达式物化成生成列再建索引,两者正好相反。
- 降序索引解决了什么问题? 联合索引中「一部分升序、一部分降序」的排序,8.0 之前只能 filesort。
- 前缀索引的长度怎么定? 让前缀区分度接近整列区分度即可,通常 10~20 个字符,配合上面的区分度 SQL 实测。
小结
结构维度回答「用什么算法组织键」,用途维度回答「这条索引服务哪个查询」。生产建索引先问三件事:查询的最左前缀是什么、能不能覆盖、写放大是否可接受;剩下的全文、空间、函数、降序索引都是特定场景的专用工具。