在 MySQL 的 InnoDB 存储引擎中,索引分为聚簇索引(Clustered Index)和非聚簇索引(Secondary Index,也称为二级索引)。它们在存储结构、数据访问方式和性能特点上有显著的区别。
1. 聚簇索引(Clustered Index)
定义
- 聚簇索引是 InnoDB 中表数据的物理存储方式。表数据按照聚簇索引的顺序存储,因此每张表只能有一个聚簇索引。
- 如果表定义了主键(Primary Key),则主键就是聚簇索引;如果没有定义主键,InnoDB 会选择一个唯一的非空索引作为聚簇索引;如果也没有这样的索引,InnoDB 会隐式创建一个隐藏的
ROWID
作为聚簇索引。
特点
- 数据与索引存储在一起:聚簇索引的叶子节点存储的是完整的数据行(即数据页),而不是指向数据的指针。
- 主键即聚簇索引:InnoDB 中,主键索引就是聚簇索引。
- 高效的范围查询:由于数据按照聚簇索引的顺序存储,范围查询(如
BETWEEN
、>
、<
)非常高效。 - 插入性能影响:如果插入的数据的主键不是顺序的,可能会导致页分裂(Page Split),影响插入性能。
示例
假设有一个表 users
,主键是 id
:
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50),
age INT
);
- 数据按照
id
的顺序存储在聚簇索引中。 - 查询
SELECT * FROM users WHERE id = 1
时,InnoDB 会直接通过聚簇索引找到对应的数据行。
2. 非聚簇索引(Secondary Index,二级索引)
定义
- 非聚簇索引是独立于聚簇索引的索引结构。它的叶子节点存储的是主键值,而不是完整的数据行。
- 一张表可以有多个非聚簇索引。
特点
- 叶子节点存储主键值:非聚簇索引的叶子节点不存储完整的数据行,而是存储主键值。通过非聚簇索引找到主键后,还需要回表(回到聚簇索引)查找完整的数据行。
- 需要回表:使用非聚簇索引查询时,如果查询的列不在索引中,InnoDB 需要通过主键值回表查找数据。
- 覆盖索引优化:如果查询的列都在非聚簇索引中(即覆盖索引),则不需要回表,可以直接从索引中获取数据。
- 适合单点查询:非聚簇索引适合等值查询(如
WHERE name = 'Alice'
),但对于范围查询,性能可能不如聚簇索引。
示例
假设在 users
表上创建一个非聚簇索引:
CREATE INDEX idx_name ON users (name);
- 索引
idx_name
的叶子节点存储的是name
和对应的主键值id
。 - 查询
SELECT * FROM users WHERE name = 'Alice'
时:- 通过
idx_name
索引找到name = 'Alice'
的记录,并获取对应的id
。 - 根据
id
回到聚簇索引中查找完整的数据行(回表)。
- 通过
3. 聚簇索引与非聚簇索引的区别
特性 | 聚簇索引(Clustered Index) | 非聚簇索引(Secondary Index) |
---|---|---|
数量 | 每张表只能有一个聚簇索引。 | 每张表可以有多个非聚簇索引。 |
存储内容 | 叶子节点存储完整的数据行。 | 叶子节点存储主键值。 |
数据与索引关系 | 数据与索引存储在一起。 | 数据与索引分离,需要回表查找数据。 |
主键 | 主键就是聚簇索引。 | 非聚簇索引的叶子节点存储主键值。 |
查询性能 | 范围查询高效,插入可能受页分裂影响。 | 等值查询高效,范围查询可能需要回表。 |
回表 | 不需要回表。 | 需要回表(除非使用覆盖索引)。 |
适用场景 | 主键查询、范围查询。 | 非主键列查询、覆盖索引优化。 |
4. 如何选择索引类型?
- 主键设计:主键应尽量短且有序(如自增整数),以减少聚簇索引的存储开销和插入时的页分裂。
- 覆盖索引:对于频繁查询的列,可以创建覆盖索引,避免回表。
- 复合索引:根据查询需求设计复合索引,充分利用最左前缀匹配原则。
- 避免过度索引:过多的非聚簇索引会增加写操作的开销。
5. 总结
- 聚簇索引:决定了数据的物理存储顺序,每张表只能有一个,主键就是聚簇索引。
- 非聚簇索引:独立于聚簇索引,叶子节点存储主键值,查询时可能需要回表。
- 优化建议:合理设计主键和索引,利用覆盖索引减少回表,提升查询性能。
通过理解聚簇索引和非聚簇索引的区别,可以更好地设计表结构和索引,从而优化 MySQL 的查询性能。
THE END
暂无评论内容