跳到主要内容

聚簇索引

InnoDB 使用聚簇索引组织完整行数据,二级索引通过聚簇索引键定位对应记录。

1. 聚簇索引叶子包含完整行

InnoDB 表按照聚簇索引的键值组织数据,聚簇索引叶子记录包含该行的所有列。通常主键就是聚簇索引,因此按主键查询到叶子页后,可以直接取得完整记录。

SELECT * FROM users WHERE id = 42;

一张 InnoDB 表只能有一个聚簇索引,因为行数据只能按一种键值顺序组织。这里的“聚簇”描述数据组织方式,不表示相邻业务数据在物理磁盘上永远连续。

2. 没有主键时 InnoDB 会选择替代键

InnoDB 按以下顺序选择聚簇索引:

  1. 使用显式定义的主键。
  2. 没有主键时,选择第一个所有列都非空的唯一索引。
  3. 两者都没有时,生成隐藏的聚簇索引键。

显式主键便于应用稳定引用记录,也让二级索引结构和迁移行为更可控。依赖隐藏键会让外部工具和数据同步缺少明确的行标识。

3. 二级索引叶子保存主键

假设 users 的主键是 id,并创建邮箱索引:

CREATE INDEX idx_users_email ON users(email);

idx_users_email 的叶子记录至少包含 email 与对应的主键 id。查询完整行时先在二级索引中找到主键,再访问聚簇索引:

SELECT * FROM users WHERE email = '[email protected]';

这两段访问通常称为回表。二级索引叶子并非只保存“索引键值”,也不保存指向固定物理地址的永久指针;保存主键使行移动或页面重组后,二级索引仍能通过逻辑键定位记录。

4. 覆盖查询可以省去回表

如果结果列已经位于二级索引中,数据库可以直接返回:

SELECT id, email
FROM users
WHERE email = '[email protected]';

此时 id 作为聚簇索引键本来就包含在二级索引叶子中,查询形成覆盖。为覆盖高频查询,也可以把少量结果列加入联合索引;列越多,索引越宽,写入、存储与缓存成本也越高。

5. 主键宽度会影响所有二级索引

二级索引叶子保存聚簇索引键,因此主键越宽,每个二级索引占用的空间通常越大,每页能够容纳的记录也越少。

主键还应保持稳定。更新主键会改变聚簇索引中的记录位置,并要求维护相关二级索引。随机分布的键可能增加页面分裂,单调递增键则可能在高并发写入时集中到树的右端。选择时要综合键宽、生成方式、写入并发和业务暴露要求。

6. 聚簇索引会影响范围与排序

按主键范围读取可以沿聚簇索引叶子页顺序扫描:

SELECT *
FROM users
WHERE id >= 10000 AND id < 10100;

其他列的范围查询仍可以使用对应二级索引,但如果返回完整行,就可能对每条记录回表。命中行数较多时,这些随机访问可能让优化器选择扫描聚簇索引。

7. 常见问题

7.1 主键索引一定是聚簇索引吗

在 InnoDB 中通常是。这个结论不能直接推广到所有数据库和存储引擎;聚簇方式属于具体实现,讨论时要说明范围。

7.2 二级索引查询一定会回表吗

不一定。查询所需列全部存在于二级索引时可以形成覆盖查询;只需要判断记录是否存在时,也可能无需读取完整行。是否回表要看查询列和执行计划。

8. 面试题

8.1 InnoDB 聚簇索引和二级索引有什么区别,什么情况下会回表

出现公司:美团

考察重点

  • 聚簇索引的行组织方式。
  • 二级索引叶子中的主键与回表路径。
  • 覆盖查询、主键宽度和范围读取。

相关内容:第 1 节“聚簇索引叶子包含完整行”至第 6 节“聚簇索引会影响范围与排序”。

参考回答

InnoDB 的聚簇索引叶子保存完整行,一张表只有一种这样的组织顺序,通常由主键承担。二级索引叶子保存二级索引列和聚簇索引键,也就是通常的主键。按二级索引查询完整行时,先找到主键,再访问聚簇索引,这一步是回表。

如果查询需要的列都在二级索引里,就能形成覆盖查询并省去回表。主键还会复制到每个二级索引叶子,因此过宽或频繁变化的主键会增加存储与维护成本。讨论这些结论时要限定 InnoDB;其他数据库或存储引擎可能采用不同组织方式。