索引基础与索引类型
本教程共 46 篇 · 第 40 篇 · 更新于 2026-07-30 · 约 13 分钟阅读
40. 索引基础与索引类型
本节目标:理解索引的本质和 B+ 树结构,搞清楚聚簇索引和二级索引的区别与回表机制,掌握各类索引的创建方式和适用场景。
40.1 索引是什么
索引(Index) 是一种数据结构,用来加速查询。就像书的目录:要找某个知识点,不用一页页翻,先查目录找到页码,直接翻过去。
没有索引时,MySQL 要从头到尾扫描整张表(全表扫描,Full Table Scan)来找数据。表越大越慢。有了索引,MySQL 可以在索引结构上快速定位,几步就能找到数据。
-- 创建索引
CREATE INDEX idx_email ON users(email);
-- 查询时自动使用索引(MySQL 优化器自动判断)
SELECT * FROM users WHERE email = 'zhangsan@test.com';
Note索引提升查询速度,但也有代价:占用额外存储空间,写入时需要同步维护索引(INSERT/UPDATE/DELETE 变慢)。索引不是越多越好。
40.2 B+ 树:索引的底层数据结构
InnoDB 的索引使用 B+ 树(B+ Tree) 数据结构。理解 B+ 树是理解索引性能的关键。
B+ 树长什么样
B+ 树是一棵多叉平衡树,特点是:
- 非叶子节点只存索引键和子节点指针,不存数据;
- 叶子节点存所有数据,且按顺序排列;
- 叶子节点之间用双向链表连接。
[30 | 60] <- 非叶子节点(只存键值)
/ | \
[10|20] [40|50] [70|80] <- 叶子节点(存数据)
<-> <-> <->
为什么用 B+ 树
| 查询方式 | 无索引(全表扫描) | B+ 树索引 |
|---|---|---|
| 100 万行找 1 行 | 扫描 100 万行 | 树高约 3 层,3 次磁盘 IO |
| 范围查询(如 id > 50) | 扫描 100 万行 | 定位到 50,沿链表往后走 |
B+ 树的关键优势:
- 矮胖:每个节点能存很多键值,树很矮(3-4 层就能存上千万行),磁盘 IO 次数少;
- 范围查询快:叶子节点是有序链表,找到起点后顺着链表走就行;
- 数据稳定:数据都在叶子节点,查询性能稳定。
TipInnoDB 默认每个页(Page)16KB。一个 B+ 树节点就是一个页。非叶子节点只存键值(假设 BIGINT 8 字节 + 指针 6 字节 = 14 字节),一个页能放约 1170 个键值。三层 B+ 树:1170 x 1170 x 16 ≈ 2100 万行,只需要 3 次磁盘 IO 就能找到任意一行。
40.3 聚簇索引 vs 二级索引
InnoDB 的索引分为两大类,这是理解索引体系的核心。
聚簇索引(Clustered Index)
聚簇索引把数据和索引存在一起,B+ 树的叶子节点直接存放完整的行数据。一张表只能有一个聚簇索引。
InnoDB 的聚簇索引就是主键索引。如果你没显式定义主键,InnoDB 会选第一个 NOT NULL 的唯一索引;如果也没有,InnoDB 会自动生成一个隐藏的 6 字节行 ID 作为聚簇索引。
聚簇索引 B+ 树:
[非叶子节点: 主键值 + 指针]
...
[叶子节点: 主键值 + 完整行数据]
二级索引(Secondary Index)
二级索引也叫非聚簇索引或辅助索引。它的 B+ 树叶子节点不存完整行数据,而是存索引列的值 + 对应的主键值。
二级索引 B+ 树(以 email 索引为例):
[非叶子节点: email 值 + 指针]
...
[叶子节点: email 值 + 主键 id]
回表(Table Lookup)
通过二级索引查到主键值后,还需要去聚簇索引里查完整行数据,这个过程叫回表。
-- 假设 email 上有二级索引
SELECT * FROM users WHERE email = 'zhangsan@test.com';
执行过程:
- 在 email 二级索引 B+ 树中查找
zhangsan@test.com,找到对应主键id = 1; - 拿
id = 1去聚簇索引 B+ 树中查找,拿到完整行数据。
两次 B+ 树查找,这就是回表的代价。
Warning回表意味着额外的磁盘 IO。如果查询只需要索引列的值,就不需要回表,这就是”覆盖索引”的概念,下一节会详细讲。
聚簇索引 vs 二级索引对比
| 特性 | 聚簇索引 | 二级索引 |
|---|---|---|
| 叶子节点存什么 | 完整行数据 | 索引列值 + 主键值 |
| 每张表数量 | 1 个 | 多个 |
| 查询是否需要回表 | 不需要 | 通常需要 |
| 建索引方式 | 自动(主键) | 手动 CREATE INDEX |
40.4 各类索引
主键索引
就是聚簇索引本身,上一节主键约束已讲过。建表时定义主键即自动创建。
唯一索引(UNIQUE Index)
索引列值不能重复,和 UNIQUE 约束本质相同:
-- 方式一:建表时定义
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(100) UNIQUE, -- 自动建唯一索引
...
);
-- 方式二:单独创建
CREATE UNIQUE INDEX idx_email ON users(email);
唯一索引既能保证数据唯一,又能加速查询,是最常用的索引类型之一。
普通索引(Normal Index)
最基本的索引,没有任何约束:
-- 创建
CREATE INDEX idx_age ON users(age);
-- 或建表时加
ALTER TABLE users ADD INDEX idx_age(age);
联合索引(Composite Index)
多列组合的索引:
CREATE INDEX idx_user_status ON orders(user_id, status);
联合索引按从左到右的顺序排列。最左前缀原则在下一节详讲。
前缀索引(Prefix Index)
对长字符串列,只索引前面一部分字符,节省空间:
-- 只索引 email 前 10 个字符
CREATE INDEX idx_email_prefix ON users(email(10));
-- 查看 email 列的值分布,选择合适的前缀长度
SELECT
COUNT(*) AS total,
COUNT(DISTINCT LEFT(email, 5)) AS prefix_5,
COUNT(DISTINCT LEFT(email, 10)) AS prefix_10,
COUNT(DISTINCT LEFT(email, 15)) AS prefix_15
FROM users;
-- 选择区分度接近完整列的前缀长度
Tip前缀索引能节省空间,但不能用于 ORDER BY 和 GROUP BY(因为索引只存了部分值)。也不能做覆盖索引。
全文索引(FULLTEXT Index)
用于全文搜索,支持自然语言分词检索:
-- 创建全文索引
CREATE FULLTEXT INDEX ft_content ON articles(content);
-- 使用全文搜索
SELECT * FROM articles
WHERE MATCH(content) AGAINST('MySQL 索引');
-- 布尔模式:支持 +(必须包含)和 -(不包含)
SELECT * FROM articles
WHERE MATCH(content) AGAINST('+MySQL -Oracle' IN BOOLEAN MODE);
Note全文索引适合文本搜索场景(如文章、商品描述)。中文需要使用 ngram 分词插件(MySQL 5.7.6+ 内置支持)。短字段(如用户名)用 LIKE 就行,不需要全文索引。
40.5 创建和删除索引
-- 方式一:CREATE INDEX
CREATE INDEX idx_age ON users(age);
CREATE UNIQUE INDEX idx_email ON users(email);
CREATE INDEX idx_multi ON orders(user_id, status);
-- 方式二:ALTER TABLE
ALTER TABLE users ADD INDEX idx_age(age);
ALTER TABLE users ADD UNIQUE INDEX idx_email(email);
ALTER TABLE users ADD FULLTEXT INDEX ft_content(content);
-- 删除索引
DROP INDEX idx_age ON users;
ALTER TABLE users DROP INDEX idx_email;
-- 查看表的索引
SHOW INDEX FROM users;
Note
SHOW INDEX FROM users的Key_name列是索引名,Column_name列是索引包含的列。主键索引的Key_name是PRIMARY。
40.6 索引的代价
| 方面 | 代价 |
|---|---|
| 空间 | 每个索引是一棵 B+ 树,占额外存储 |
| 写入 | INSERT/UPDATE/DELETE 需要同步维护所有索引 |
| 优化器选择 | 索引太多,优化器可能选错索引 |
Tip一个经验法则:一张表的索引数量控制在 5-6 个以内。经常查询的列建索引,很少查询的列不建。联合索引能覆盖多个查询场景,比建多个单列索引更高效。
40.7 小结
- 索引是加速查询的数据结构,InnoDB 用 B+ 树实现;
- B+ 树矮胖有序,3-4 层就能存千万行数据,查询只需 3-4 次磁盘 IO;
- 聚簇索引(主键)叶子节点存完整行数据,一张表只有一个;
- 二级索引叶子节点存索引列值 + 主键值,查询完整行需要回表;
- 常用索引类型:主键索引、唯一索引、普通索引、联合索引、前缀索引、全文索引;
- 索引有空间和写入代价,不是越多越好,控制在 5-6 个以内。
下一节讲索引设计原则和最左前缀原则,学会怎么科学地建索引。