面试官:请问在 MySQL 中,什么是聚簇索引(主键索引)和非聚簇索引(二级索引)?它们有什么区别?

面试回答

“在 MySQL 的 InnoDB 存储引擎中,索引主要分为聚簇索引非聚簇索引(也叫二级索引或辅助索引)。

简单来说,聚簇索引就是表数据本身。它的 B+ 树叶子节点直接存储了整行的完整数据。因为数据和索引是绑定在一起存放的,所以一张表只能有一个聚簇索引,通常就是主键索引。当我们通过主键查询时,能直接在叶子节点拿到完整数据,速度非常快。

非聚簇索引是根据普通列建立的索引。它的叶子节点不存整行数据,而是存储了该索引列的值以及对应的主键值。当我们通过二级索引查询时,会先在二级索引树中找到对应的主键值,然后再拿着这个主键值去聚簇索引树中查找完整的行数据,这个过程被称为回表。”

系统讲解

核心对比

特性聚簇索引 (Clustered Index)非聚簇索引 / 二级索引 (Secondary Index)
叶子节点存储内容完整的行数据(Row Data)索引列的值 + 对应的主键值
数量限制每张表只能有一个(通常是主键)每张表可以有多个
查询过程直接在叶子节点获取数据,无需回表先查到主键,再根据主键去聚簇索引查数据(回表),除非触发了覆盖索引
物理存储数据行和相邻的键值紧凑地存储在一起独立于数据行存储

底层结构示意

假设有一张用户表 User(id, name, age),其中 id 是主键,name 字段上建了普通索引。

  1. 聚簇索引(id 上的索引)

    • 非叶子节点:存储 id 的范围值和指针。
    • 叶子节点:存储完整的行数据,例如 [id=1, name='Alice', age=25]
  2. 非聚簇索引(name 上的索引)

    • 非叶子节点:存储 name 的范围值和指针。
    • 叶子节点:存储 name 的值和对应的主键 id,例如 [name='Alice', id=1]

亮点与深度

1. InnoDB 是如何选择聚簇索引的?

虽然聚簇索引通常是主键,但 InnoDB 内部有一套严格的选择规则:

  1. 如果表定义了 PRIMARY KEY,InnoDB 会使用它作为聚簇索引。
  2. 如果没有定义主键,InnoDB 会选择第一个不包含 NULL 值的唯一索引UNIQUE NOT NULL)作为聚簇索引。
  3. 如果既没有主键,也没有合适的唯一索引,InnoDB 会自动生成一个隐式的、6 字节的 ROW_ID 作为聚簇索引。

2. 什么是回表?如何避免回表?

回表是指:通过非聚簇索引查询到主键值后,还需要再回到聚簇索引树中查找完整行数据的过程。这会多产生一次 B+ 树的扫描,影响查询性能。

避免回表的方法是使用覆盖索引(Covering Index)。 如果一个查询语句中,需要查询的所有列都已经包含在非聚簇索引的树中(例如 SELECT id, name FROM User WHERE name = 'Alice'),那么在遍历 name 索引树时就能直接拿到 idname,不需要再去聚簇索引查完整数据,这就叫覆盖索引,能极大提升查询效率。