聚簇索引、二级索引、回表与覆盖索引分别是什么?
结论先行:InnoDB 中聚簇索引的叶子节点直接存整行数据,二级索引的叶子只存索引列 + 主键;通过二级索引查非索引列要回聚簇索引再取一次,称为回表;若查询列都在二级索引里则无需回表,称为覆盖索引。
一、两类索引的本质区别
| 对比项 | 聚簇索引(主键索引) | 二级索引(辅助索引) |
|---|---|---|
| 叶子内容 | 完整行记录 | 索引列值 + 主键值 |
| 每表数量 | 只能一个 | 可以多个 |
| 数据组织 | 行按主键物理顺序存放 | 逻辑有序,需按主键回表 |
| 常见举例 | PRIMARY KEY(id) | KEY idx_name(name) |
二、回表是怎么发生的
SELECT * FROM user WHERE name = 'zhang';
1) 走二级索引 idx_name,找到主键 id = 18
2) 拿主键 18 再去聚簇索引 B+Tree 查整行 ← 这一步就是回表
- 回表本质是两次 B+Tree 查找,索引区分度低或命中行多时代价会被放大。
- 优化方向:让二级索引覆盖目标列,或用索引下推减少回表行数。
三、覆盖索引:把查询列装进索引
-- idx(name, age):二级索引里同时有 name 与 age
SELECT name, age FROM user WHERE name = 'zhang'; -- 无需回表
-- 只给 age 条件,违反最左前缀,通常走不到 idx(name, age)
SELECT name, age FROM user WHERE age > 20;
- 覆盖索引 = 查询所需列全部存在于同一个二级索引,Extra 显示 Using index。
- 代价:索引里冗余的列越多,写入与空间开销越大,只覆盖高频查询列即可。
四、索引下推(ICP)与联合索引的配合
- 联合索引按最左前缀生效(见第 28 章),覆盖列尽量从左往右设计。
- MySQL 5.6+ 支持索引下推:在二级索引内先过滤部分条件,减少回表行数。
常见追问与记忆点
- 追问:InnoDB 为什么必须有聚簇索引?没有主键就用首个非空唯一键,再没有则隐藏生成 ROWID。
- 追问:回表一定慢吗?不一定,命中行少且都在内存页时代价很小,量大才成问题。
- 记忆点:聚簇 = 叶子存行,二级 = 叶子存主键;回表 = 拿主键再查一次,覆盖 = 列全在索引里。