聚簇索引即主键,由InnoDB自动确定:优先用PRIMARY KEY,其次选UNIQUE NOT NULL列,最后隐式生成row_id;每表仅一个,决定数据物理存储顺序,叶子节点存整行数据,非聚簇索引叶子只存索引列和主键值。

聚簇索引就是主键,不是选出来的,是“定下来的”
在InnoDB中,PRIMARY KEY自动成为聚簇索引;没定义主键时,InnoDB会找第一个UNIQUE NOT NULL列顶上;连这个都没有,就用隐式生成的GEN_CLUST_INDEX(6字节row_id)。它不是你用CREATE INDEX建出来的——CREATE CLUSTERED INDEX在MySQL里根本不存在,那是SQL Server的语法,MySQL不支持。
常见错误:以为加个CREATE INDEX idx_name ON t(name)就能变成聚簇索引。实际它只是非聚簇索引,哪怕name是唯一且非空的。
- 聚簇索引决定数据物理存储顺序,所以每张表只能有一个
- 修改
PRIMARY KEY值会触发整行数据移动+所有二级索引更新,代价极高,应避免 - 用
UUID或随机字符串当主键,会导致页分裂严重、空间碎片多、写性能下降
非聚簇索引查数据要“回表”,两次B+树查找很常见
比如执行SELECT * FROM user WHERE name = '张三',而name上有非聚簇索引:
- 第一步:在
name索引树里找到'张三'对应的叶子节点,拿到它的id(主键值) - 第二步:拿着这个
id再去聚簇索引树里查一次,才能取到完整行数据 - 这个“先查索引再查主键”的过程叫回表(Secondary Lookup),I/O开销翻倍
如果只查索引列本身,比如SELECT name, email FROM user WHERE name = '张三',且(name, email)是联合索引,那可能走覆盖索引,跳过回表——但前提是email也在索引定义里。
聚簇索引叶子存整行,非聚簇索引叶子只存主键
这是结构差异的核心:
- 聚簇索引叶子节点:直接存
id、name、email……全部字段,外加事务ID、回滚指针等内部字段 - 非聚簇索引叶子节点:只存索引列值(如
name) + 对应行的PRIMARY KEY值(如id) - 所以主键越短,非聚簇索引越紧凑;
INT主键比VARCHAR(36)UUID省得多
这也是为什么SELECT *走非聚簇索引一定回表,而SELECT id可能直接从非聚簇索引叶子拿到结果——只要id是主键,它就在叶子节点里。
范围查询时,聚簇索引天然有序,非聚簇索引要额外排序
WHERE id BETWEEN 100 AND 200这种查询,聚簇索引能顺着叶子链表连续读取,I/O少、缓存友好;但WHERE name BETWEEN 'A' AND 'M'即使name有非聚簇索引,也只能先定位起止位置,再逐条拿id去聚簇索引查——而且这些id在磁盘上大概率不连续,随机I/O多。
- 聚簇索引的有序性还直接影响
ORDER BY id和LIMIT offset, size的效率 - 基于游标分页(
WHERE id > last_id LIMIT 20)比OFFSET快得多,正是依赖聚簇索引的物理有序 - 非聚簇索引无法提供这种物理局部性,对高偏移量分页尤其不友好
真正容易被忽略的是:聚簇索引不是“更快的索引”,而是“数据本身”。一旦理解这点,就不会纠结“该不该给某个字段建聚簇索引”——你只能选谁来当它,不能新建第二个。


















