MySQL 中聚簇索引和二级索引有什么区别?
简化版
InnoDB 的聚簇索引通常就是主键索引,叶子节点保存整行数据;二级索引叶子节点保存二级索引列和主键值,查询非覆盖字段时需要再按主键回表。
详细版
聚簇索引决定数据行的物理组织方式。在 InnoDB 中,一张表的数据按主键 B+ 树组织,主键索引叶子节点就是完整行数据。
二级索引也使用 B+ 树,但它的叶子节点不直接保存完整行,而是保存二级索引键和对应主键值。比如 idx_name(name) 查到 name='Tom' 后,若还要读取 age、address 等不在索引里的列,就需要拿主键再去聚簇索引查一次,这叫回表。
面试回答要带出三个点:InnoDB 表数据和主键索引绑定;二级索引通过主键定位整行;主键设计会影响所有二级索引的空间和查询成本。
完整版教学
一、为什么叫聚簇索引
聚簇索引的意思是“数据行和索引放在一起”。在 InnoDB 中,主键索引的叶子节点保存整行记录。
可以这样理解:
主键 B+ 树
非叶子节点:主键范围导航
叶子节点:id + name + age + address + ...
所以根据主键查一行通常非常直接:
SELECT * FROM users WHERE id = 100;
数据库沿主键 B+ 树找到叶子节点,就拿到了整行数据。
记忆钩子:聚簇索引叶子节点就是数据,二级索引叶子节点只是通往数据的路标。
二、二级索引叶子节点保存什么
二级索引是除聚簇索引之外的普通索引、唯一索引等。在 InnoDB 中,二级索引叶子节点保存的是“索引列 + 主键值”。
例如:
CREATE TABLE users (
id BIGINT PRIMARY KEY,
name VARCHAR(50),
age INT,
city VARCHAR(50),
INDEX idx_name(name)
);
idx_name 的叶子节点近似是:
name='Alice' -> id=10
name='Bob' -> id=18
name='Tom' -> id=25
如果查询 SELECT id, name FROM users WHERE name='Tom',二级索引本身已经有 name 和 id,可能不需要回表。如果查询 SELECT city FROM users WHERE name='Tom',就要拿 id=25 回到聚簇索引读取 city。
三、什么是回表
回表就是“先走二级索引找到主键,再走聚簇索引取整行或缺失列”。
SELECT city
FROM users
WHERE name = 'Tom';
执行路径:
idx_name 二级索引:name='Tom' -> id=25
聚簇索引:id=25 -> 读取 city
如果命中 1 行,回表成本不明显;如果命中 10 万行,每一行都要回表,随机访问成本就可能很高。
这也是为什么覆盖索引能优化查询:如果所需字段都在二级索引里,就不用再回到聚簇索引。
四、主键为什么会影响二级索引
因为每个二级索引叶子节点都保存主键值,所以主键越大,二级索引越占空间。
假设一张表有 1000 万行、5 个二级索引:
| 主键类型 | 主键长度 | 二级索引额外保存成本 |
|---|---|---|
BIGINT | 8 字节 | 较小 |
CHAR(36) UUID | 36 字节 | 明显更大 |
| 长字符串 | 64 字节以上 | 成本更高 |
这还只是空间成本,页中能容纳的索引项变少后,B+ 树高度和缓存命中率也可能受影响。
因此 InnoDB 常建议使用短小、稳定、尽量递增的主键,尤其是写入量大、二级索引多的业务表。
五、没有主键时 InnoDB 怎么办
InnoDB 优先使用主键作为聚簇索引。如果没有显式主键,会选择第一个非空唯一索引;如果也没有合适唯一索引,InnoDB 会生成隐藏 row id 作为聚簇索引键。
流程可以记成:
显式 PRIMARY KEY
-> 第一个 NOT NULL UNIQUE KEY
-> 隐藏 row id
隐藏 row id 对业务不可见,不利于查询和维护。工程上不建议依赖它,最好为表设计明确主键。
如果业务天然有稳定唯一字段,也要考虑是否适合作为主键。例如手机号会变更,身份证号敏感且较长,很多时候使用业务无关的自增或雪花 id 更可控。
六、聚簇索引对范围查询和插入的影响
主键顺序决定数据在聚簇索引中的排列。递增主键插入通常追加到 B+ 树右侧,页分裂较少;随机 UUID 主键会把新记录插入到各个位置,可能造成页分裂和缓存抖动。
范围查询也会受益于聚簇索引顺序:
SELECT *
FROM orders
WHERE id BETWEEN 100000 AND 100100;
因为叶子节点按主键有序链接,范围扫描可以顺序读取相邻叶子页。若按随机主键写入,数据逻辑顺序仍然有序,但插入维护成本更高。
这就是主键设计题经常和聚簇索引一起考的原因。
七、常见误区与追问
- 误区:聚簇索引就是一种普通索引。 在 InnoDB 中它同时组织整行数据,和表数据强绑定。
- 误区:二级索引叶子节点保存整行数据。 InnoDB 二级索引叶子节点保存索引列和主键值,不保存完整行。
- 误区:所有二级索引查询都会回表。 如果查询字段被索引覆盖,就可以不回表。
- 误区:主键长度只影响主键索引。 二级索引叶子节点也保存主键,主键长度会影响所有二级索引。
- 追问:为什么不建议随机 UUID 做 InnoDB 主键? 它较长且插入位置随机,可能增加页分裂、空间占用和缓存压力。
- 追问:没有主键时会怎样? InnoDB 会尝试选非空唯一索引,否则生成隐藏 row id,但工程上应显式设计主键。
八、加强记忆
聚簇索引题抓住“一棵树存整行,二级索引存主键”。主键查直接到数据,二级索引查常要回表;主键既决定行组织方式,又进入每个二级索引,所以主键设计会影响查询、空间和写入成本。