mysql系列之聚集索引和非聚集索引

5 阅读4分钟

InnoDB 的索引按数据存储方式分为 聚集索引 和 非聚集索引(也称为二级索引),两者在结构、查找效率和使用场景上有着本质区别。


1. 聚集索引(Clustered Index)

定义

聚集索引决定了表中数据的物理存储顺序。InnoDB 表的数据本身就是按照聚集索引组织的,叶子节点直接存储完整的行记录

规则

InnoDB 会按以下优先级选择聚集索引:

  1. 如果表有 PRIMARY KEY,则使用主键作为聚集索引。
  2. 如果没有主键,则使用第一个 UNIQUE NOT NULL 索引作为聚集索引。
  3. 如果以上都没有,InnoDB 自动生成一个隐式的 6 字节 ROW_ID 作为聚集索引。

特点

  • 每个 InnoDB 表有且只有一个聚集索引。
  • 叶子节点存储整行数据(所有列),数据页之间通过双向链表连接。
  • 索引顺序 = 数据物理顺序,范围查询非常高效。

结构示意

text

聚集索引树(B+树)
    根节点
    /     \
  内节点  内节点
  /   \   /   \
叶子节点(包含完整行数据)

2. 非聚集索引(Secondary Index)

定义

非聚集索引是建立在非主键列上的索引,它的叶子节点不存储完整行数据,而是存储索引列的值 + 对应行的主键值(或聚集索引键)。

特点

  • 一个表可以有多个非聚集索引。
  • 叶子节点只存储索引键和主键值,不包含其他列。
  • 当通过非聚集索引查询时,若需要访问索引中未包含的列,则需要先获取主键值,再通过回表到聚集索引中获取完整行数据。

结构示意

text

非聚集索引树(B+树)
    根节点
    /     \
  内节点  内节点
  /   \   /   \
叶子节点(索引键 + 主键值)

3. 回表与覆盖索引

回表

当查询使用非聚集索引,但需要返回的列不在该索引中时,InnoDB 会:

  1. 在非聚集索引中找到符合条件的叶子节点,得到主键值。
  2. 再用主键值到聚集索引中查找完整行记录,这个过程称为“回表”。

回表会增加一次额外的 I/O,因此效率低于直接在聚集索引上查找。

覆盖索引

如果查询需要的所有列都包含在某个非聚集索引的键中,那么 InnoDB 可以直接从非聚集索引返回结果,无需回表,这样的索引称为覆盖索引。

例如:

sql

CREATE TABLE t (id INT PRIMARY KEY, name VARCHAR(10), age INT, INDEX idx_name (name));
-- 覆盖索引查询
SELECT name FROM t WHERE name = '张三';   -- 只需 idx_name 即可
-- 需要回表
SELECT age FROM t WHERE name = '张三';    -- 需回表取 age

4. 聚集索引 vs 非聚集索引 对比

维度聚集索引非聚集索引
数量1 个多个
叶子节点内容完整行数据索引列 + 主键值
数据存储顺序与索引顺序一致独立于数据顺序
查找效率主键查找极快,范围查询高效可能回表,比聚集索引多一次 I/O
插入性能插入顺序可能引起页分裂(若主键无序)插入主要维护索引树,相对轻量
占用空间数据本身占空间,无额外存储额外占用存储空间

5. 设计建议

  • 合理选择主键:建议使用自增整数或有序的 UUID(如 UUID v7)作为主键,避免频繁的页分裂,保证插入性能。
  • 利用覆盖索引:对于高频查询,尽量将需要返回的列包含到索引中,避免回表。
  • 控制二级索引数量:每个二级索引都会增加插入、更新、删除的开销,需权衡查询性能与写性能。

6. 与其他存储引擎对比

  • MyISAM:无论主键还是普通索引,都是非聚集索引。数据和索引分离,索引叶子节点存储的是行指针(文件偏移量),而不是主键值。
  • InnoDB 的聚集索引设计使得主键查询极快,但二级索引需要存储主键值,因此主键大小直接影响所有二级索引的存储空间。

总结

聚集索引是 InnoDB 的基石,它将数据和索引融为一体,为快速主键访问和范围扫描提供了天然优势;非聚集索引则通过存储主键值来间接定位数据,灵活支持各种查询,但需注意回表开销。理解这一机制有助于优化表结构和索引设计,提升数据库整体性能。