InnoDB 的索引按数据存储方式分为 聚集索引 和 非聚集索引(也称为二级索引),两者在结构、查找效率和使用场景上有着本质区别。
1. 聚集索引(Clustered Index)
定义
聚集索引决定了表中数据的物理存储顺序。InnoDB 表的数据本身就是按照聚集索引组织的,叶子节点直接存储完整的行记录。
规则
InnoDB 会按以下优先级选择聚集索引:
- 如果表有 PRIMARY KEY,则使用主键作为聚集索引。
- 如果没有主键,则使用第一个 UNIQUE NOT NULL 索引作为聚集索引。
- 如果以上都没有,InnoDB 自动生成一个隐式的 6 字节 ROW_ID 作为聚集索引。
特点
- 每个 InnoDB 表有且只有一个聚集索引。
- 叶子节点存储整行数据(所有列),数据页之间通过双向链表连接。
- 索引顺序 = 数据物理顺序,范围查询非常高效。
结构示意
text
聚集索引树(B+树)
根节点
/ \
内节点 内节点
/ \ / \
叶子节点(包含完整行数据)
2. 非聚集索引(Secondary Index)
定义
非聚集索引是建立在非主键列上的索引,它的叶子节点不存储完整行数据,而是存储索引列的值 + 对应行的主键值(或聚集索引键)。
特点
- 一个表可以有多个非聚集索引。
- 叶子节点只存储索引键和主键值,不包含其他列。
- 当通过非聚集索引查询时,若需要访问索引中未包含的列,则需要先获取主键值,再通过回表到聚集索引中获取完整行数据。
结构示意
text
非聚集索引树(B+树)
根节点
/ \
内节点 内节点
/ \ / \
叶子节点(索引键 + 主键值)
3. 回表与覆盖索引
回表
当查询使用非聚集索引,但需要返回的列不在该索引中时,InnoDB 会:
- 在非聚集索引中找到符合条件的叶子节点,得到主键值。
- 再用主键值到聚集索引中查找完整行记录,这个过程称为“回表”。
回表会增加一次额外的 I/O,因此效率低于直接在聚集索引上查找。
覆盖索引
如果查询需要的所有列都包含在某个非聚集索引的键中,那么 InnoDB 可以直接从非聚集索引返回结果,无需回表,这样的索引称为覆盖索引。
例如:
sql
CREATE TABLE t (id INT PRIMARY KEY, name VARCHAR(10), age INT, INDEX idx_name (name));
-- 覆盖索引查询
SELECT name FROM t WHERE name = '张三'; -- 只需 idx_name 即可
-- 需要回表
SELECT age FROM t WHERE name = '张三'; -- 需回表取 age
4. 聚集索引 vs 非聚集索引 对比
| 维度 | 聚集索引 | 非聚集索引 |
|---|---|---|
| 数量 | 1 个 | 多个 |
| 叶子节点内容 | 完整行数据 | 索引列 + 主键值 |
| 数据存储顺序 | 与索引顺序一致 | 独立于数据顺序 |
| 查找效率 | 主键查找极快,范围查询高效 | 可能回表,比聚集索引多一次 I/O |
| 插入性能 | 插入顺序可能引起页分裂(若主键无序) | 插入主要维护索引树,相对轻量 |
| 占用空间 | 数据本身占空间,无额外存储 | 额外占用存储空间 |
5. 设计建议
- 合理选择主键:建议使用自增整数或有序的 UUID(如 UUID v7)作为主键,避免频繁的页分裂,保证插入性能。
- 利用覆盖索引:对于高频查询,尽量将需要返回的列包含到索引中,避免回表。
- 控制二级索引数量:每个二级索引都会增加插入、更新、删除的开销,需权衡查询性能与写性能。
6. 与其他存储引擎对比
- MyISAM:无论主键还是普通索引,都是非聚集索引。数据和索引分离,索引叶子节点存储的是行指针(文件偏移量),而不是主键值。
- InnoDB 的聚集索引设计使得主键查询极快,但二级索引需要存储主键值,因此主键大小直接影响所有二级索引的存储空间。
总结
聚集索引是 InnoDB 的基石,它将数据和索引融为一体,为快速主键访问和范围扫描提供了天然优势;非聚集索引则通过存储主键值来间接定位数据,灵活支持各种查询,但需注意回表开销。理解这一机制有助于优化表结构和索引设计,提升数据库整体性能。