在 MySQL(以 InnoDB 存储引擎 为主流实现)中,索引的核心结构是 B+Tree 。下面我将从整体概念 → 物理结构 → 不同索引类型 → 查询过程 → 设计要点几个层次,系统地讲解 MySQL 的索引结构。
一、为什么 MySQL 使用 B+Tree 作为索引结构
索引的本质是 加速数据定位的数据结构。数据库索引需要同时满足:
-
磁盘友好(减少 I/O 次数)
-
支持范围查询(
BETWEEN、> <、ORDER BY) -
支持高效查找、插入、删除
常见数据结构对比
| 结构 | 优点 | 缺点 |
|---|---|---|
| 数组 | 查找快(O(1)) | 插入、删除代价高 |
| 链表 | 插入快 | 查找慢 |
| 二叉搜索树 | 有序 | 容易退化 |
| 红黑树 | 平衡 | 树高大,磁盘 I/O 多 |
| B+Tree | 低树高 + 顺序访问友好 | 结构稍复杂 |
结论:B+Tree 是磁盘数据库索引的最优解之一。
二、B+Tree 在 InnoDB 中的物理结构
1. 基本特征
InnoDB 的 B+Tree 具有以下特点:
-
非叶子节点只存索引键,不存数据
-
所有数据都存放在叶子节点
-
叶子节点之间通过双向链表连接
-
树是多叉平衡树(不是二叉)
示意结构:
[ 10 | 20 ]
/ | \
[1,5] [10,15] [20,30]
| | |
叶子节点(存数据,且有链表)
2. 页(Page)是最小存储单位
InnoDB 并不是一条一条存数据,而是:
-
以页(Page)为单位存储
-
默认页大小:16KB
一个 B+Tree 节点 ≈ 一个 Page
一个 Page 可以存放:
多个索引 key
或多行数据(叶子节点)
三、聚簇索引(Clustered Index)
1. 什么是聚簇索引
InnoDB 中:
表数据本身就是一棵 B+Tree
这棵树的 key 是 主键
这棵树叫做:聚簇索引
2. 聚簇索引的结构
-
叶子节点:存储完整的一行数据
(逻辑上存放一行数据,物理上实际存放一页数据,叶子节点就是数据本身) -
非叶子节点:存主键值 + 指向子节点的指针
示意:
主键B+Tree(聚簇索引)
叶子节点:
[ id=1 | row data ]
[ id=5 | row data ]
[ id=10| row data ]
3. 关键结论
-
一张 InnoDB 表 有且只有一个聚簇索引
-
通常是
PRIMARY KEY -
如果没有主键:
-
选唯一非空索引
-
再没有 → 隐式生成 row_id
-
四、二级索引(Secondary Index / 非聚簇索引)
1. 二级索引的结构
二级索引同样是 B+Tree,但叶子节点存的不是整行数据,而是:
索引列值 + 主键值
示意:
name 索引 B+Tree(二级索引)
叶子节点:
[ "Alice" | id=5 ]
[ "Bob" | id=10 ]
2. 回表
当使用二级索引查询时:
-
在二级索引中查到 主键 id
-
再回到 聚簇索引 中查整行数据
这一步叫:回表
五、一次查询的索引查找过程示例
假设有表:
CREATE TABLE user (
id INT PRIMARY KEY,
name VARCHAR(50),
age INT,
INDEX idx_name(name)
);
表中的字段为(id , name ,age),其中id为主键值,此外还有一个二级索引,存储了(主键列值name, 主键值id)
查询 1:主键查询
指在 SQL 中通过表的主键(PRIMARY KEY)作为查询条件,直接在聚簇索引上定位并读取目标行数据的查询方式
SELECT * FROM user WHERE id = 10;
过程:
-
直接在 聚簇索引 上查
-
一次 B+Tree 查找
-
不回表
查询 2:二级索引查询
指 SQL 查询通过非主键索引(Secondary Index)作为访问路径,先在二级索引 B+Tree 中定位记录,再(通常)通过主键回到聚簇索引获取完整行数据的查询方式
SELECT * FROM user WHERE name = 'Bob';
过程:
-
在
idx_name中查到id = 10 -
回到主键 B+Tree 查询整行数据
→ 2 次索引查找
查询 3:覆盖索引
是指一次查询中,所需的所有列都能直接从某个索引中获得 ,不需要回到聚簇索引读取整行数据的查询方式
SELECT id FROM user WHERE name = 'Bob';
-
idx_name叶子节点已经包含id -
不需要回表
-
这叫 覆盖索引,
六、B+Tree 对范围查询的支持
由于叶子节点是 有序链表:
SELECT * FROM user WHERE id BETWEEN 10 AND 100;
流程:
-
找到 id=10
-
顺着叶子节点链表顺序扫描
-
非常高效(顺序 I/O)
这也是 B+Tree 相比 Hash 索引的重要优势。
七、其他索引结构(简要)
1. Hash 索引(Memory 引擎)
-
基于哈希表
-
只能等值查询
-
不支持范围 / 排序
-
InnoDB 不直接支持(自适应 Hash 除外)
2. 全文索引(FULLTEXT)
-
使用倒排索引
-
适用于文本搜索
-
与 B+Tree 索引完全不同
八、索引设计中的几个关键结论
1. 主键设计
-
主键 越短越好
-
尽量使用 自增整数
-
避免 UUID(会导致页分裂)
2. 联合索引与最左前缀
INDEX idx_a_b_c (a, b, c)
-
可用:
-
a -
a, b -
a, b, c
-
-
不可用:
-
b -
c
-
最左前缀
当使用联合索引(Composite Index)时,MySQL 只能从索引的最左列开始,连续地使用索引列 来进行查找;
一旦中间断开或跳过某一列,该列及其右侧列将无法再用于索引定位。
3. 索引不是越多越好
-
占用磁盘空间
-
写操作成本增加
-
优化器选择成本上升
九、一句话总结
MySQL(InnoDB)的索引本质是 B+Tree:
主键索引是聚簇索引,数据存于叶子节点;
二级索引的叶子节点存主键,需要回表;
叶子节点链表结构使范围查询极其高效。