← 返回题目列表

聚簇索引和二级索引有什么区别?什么是「回表」?

中等 第 16 / 25 题 更新于 2026/07/28
B+树聚簇索引二级索引回表

简化版

在 InnoDB 里,索引都是 B+ 树,区别在叶子节点存什么聚簇索引(主键索引)的叶子存整行数据,数据就跟着主键一起组织,一张表只有一个;二级索引(非聚簇/辅助索引)的叶子只存主键值。用二级索引查询时,先查到主键,再拿主键回聚簇索引查整行——这个「查两棵树」的过程叫回表。如果二级索引已包含要查的列,就不用回表,叫覆盖索引

详细版

聚簇索引(Clustered Index)

  • 叶子节点直接存放整行数据,表数据本身就按主键顺序组织在这棵 B+ 树里。
  • 一张表只有一个聚簇索引(就是主键;没主键则用唯一非空索引,再没有则 InnoDB 内部生成隐藏 rowid)。
  • 「聚簇」= 数据和主键索引聚在一起。

二级索引(Secondary Index,非聚簇索引)

  • 叶子节点存索引列 + 主键值(不存整行数据)。
  • 可以有多个(每个非主键索引都是一棵二级索引 B+ 树)。

回表(Back to table)

SELECT * FROM t WHERE name = 'Tom'    (name 上有二级索引)
1) 在 name 的二级索引 B+ 树里查到 'Tom' → 得到主键 id = 100
2) 再拿 id=100 去聚簇索引 B+ 树里查出整行  ← 这一步就是「回表」

完整版教学

一、两种索引的本质区别:叶子存什么

InnoDB 的所有索引都是 B+ 树,结构一样,唯一区别在叶子节点存的内容

  • 聚簇索引叶子 = 整行数据。所以在 InnoDB 里,「表」和「主键索引」是同一个东西——数据就是按主键组织成的 B+ 树,没有独立的「数据文件」。
  • 二级索引叶子 = 主键值(加索引列本身)。它不存整行,只存「怎么找到整行的钥匙(主键)」。

二、为什么二级索引存主键而不是行地址

二级索引叶子存的是主键值,而不是「行的物理地址」。这样设计的好处是:当某行数据因为页分裂、更新而物理位置改变时,只需维护聚簇索引,二级索引不用改(主键值没变)。代价就是查询要多一步「用主键回聚簇索引找行」——即回表。这是一个「牺牲一点查询速度、换取数据移动时的维护简单」的权衡。

三、回表:二级索引查询的额外代价

用二级索引查询非索引列时,要走两棵 B+ 树:

  1. 先在二级索引里按条件查到主键值。
  2. 再拿主键值去聚簇索引里查出完整的行。

第二步就是回表。它意味着一次二级索引查询实际上要查两棵树、多几次 IO。如果结果行很多,回表次数多,开销显著(这也是「深分页」「大范围二级索引查询」慢的原因之一)。

四、覆盖索引:避免回表

如果一个查询需要的列,二级索引里全都有(索引列 + 主键),就不用回表——直接在二级索引这棵树里就取到了全部结果。这叫覆盖索引(Covering Index)

-- name 上有二级索引,叶子存 (name, id)
SELECT id, name FROM t WHERE name = 'Tom';   -- 只要 id 和 name
-- 二级索引叶子里就有 name 和 id,不用回表 → 覆盖索引,快

所以实践中常通过建联合索引把常查的列包进去,让查询「被索引覆盖」,避免回表。EXPLAIN 里看到 Using index 就表示用上了覆盖索引。

五、对建表和查询的启示

  • SELECT * 容易触发回表:要了不在索引里的列,就得回表;只 SELECT 需要的列更可能命中覆盖索引。
  • 主键要短:二级索引叶子都存了主键,主键越大,每个二级索引也越大。短主键让所有二级索引都受益。
  • 联合索引的列顺序:把查询和排序常用的列放进联合索引,能同时利用最左前缀 + 覆盖索引,减少回表。

六、常见误区与追问

考点正确口径
聚簇索引叶子节点存整行数据
二级索引叶子节点存索引列和主键值
回表用二级索引找到主键后再查聚簇索引取整行
secondary index (idx_name):
  name -> primary_key
clustered index:
  primary_key -> full row

回表不是多查一次二级索引,而是拿二级索引里的主键再去聚簇索引查整行。

  • 误区:二级索引叶子节点直接存整行。 InnoDB 二级索引叶子通常存主键值,完整行在聚簇索引叶子页。
  • 误区:使用二级索引一定要回表。 如果查询列都在二级索引中,形成覆盖索引,就不需要回表。
  • 误区:聚簇索引可以有很多个。 表的数据物理组织只能按一种聚簇方式存放,通常聚簇索引只有一个。
  • 追问:为什么二级索引存主键而不是行地址? 主键稳定表达行位置,页分裂移动时不需要更新所有二级索引的物理地址。
  • 追问:回表代价为什么高? 二级索引查一次 B+ 树后,还要按主键再查聚簇索引,随机 IO 和页访问增加。
  • 追问:如何减少回表? 设计覆盖索引、减少 select *、让查询列尽量被索引包含。

七、加强记忆

InnoDB 索引都是 B+ 树,区别在叶子:聚簇索引(主键)叶子存整行数据,一表一个,数据即索引;二级索引叶子存主键值,可多个。用二级索引查非索引列,要先查到主键、再回聚簇索引取整行,这叫回表(查两棵树)。若二级索引已包含所需全部列,则覆盖索引,免回表。启示:少用 SELECT *、主键要短、善用联合索引凑覆盖。