1. 聚集索引一个表只能有一个,而非聚集索引一个表可以存在多个
  2. 聚集索引存储记录是物理上连续存在,而非聚集索引是逻辑上的连续,物理存储并不连续
  3. 聚集索引:物理存储按照索引排序;聚集索引是一种索引组织形式,索引的键值逻辑顺序决定了表数据行的物理存储顺序
  4. 非聚集索引:物理存储不按照索引排序;非聚集索引则就是普通索引了,仅仅只是对数据列创建相应的索引,不影响整个表的物理存储顺序.
  5. 索引是通过二叉树的数据结构来描述的,我们可以这么理解聚簇索引:索引的叶节点就是数据节点。而非聚簇索引的叶节点仍然是索引节点,只不过有一个指针指向对应的数据块。

五条特征准确,但还差三块把区别变成可用知识:回表与覆盖索引的代价模型、聚簇键的选择原则、以及”物理连续”的准确含义(页内连续而非物理相邻)。本文补全。

结构再确认:叶节点存什么

  • 聚簇索引(clustered):叶节点 = 完整行数据;表数据按聚簇键组织成一棵 B+Tree——“表即索引”
  • 非聚簇索引(secondary):叶节点 = 索引键 + 聚簇键值(InnoDB);是独立于表数据的第二棵树

一个表只能有一个聚簇索引的原因由此自明:行的物理组织方式只能有一种;而二级索引可以有多个(每种键序一棵树)。

“物理连续”的准确含义

聚簇索引的”连续”指聚簇键顺序上的页内/逻辑连续(页间以指针相连、尽量Extent 顺序分配),而非磁盘物理扇区相邻。它带来的真实收益是:按聚簇键的范围扫描是顺序 IO;代价是:聚簇键无序插入(如 UUID)导致页分裂与随机 IO——这是聚簇键选型的全部要害。

回表与覆盖索引:代价模型

二级索引查询的两种结局:

  • 查询列 ⊆ 二级索引列(含聚簇键)→ 覆盖索引,叶节点直接取数,一次树下行
  • 否则 → 回表:拿叶中聚簇键再走一次聚簇树(第二次树下行,且是随机 IO)
-- idx_age 二级索引
SELECT id, age FROM t WHERE age = 20; -- 覆盖:索引含 age+id(聚簇键)
SELECT id, age, name FROM t WHERE age = 20; -- 回表:name 不在索引中

优化动作:把高频查询列加入联合索引实现覆盖(EXPLAIN Extra: Using index 验证);回表行数大时优化器可能弃用二级索引改全表扫(随机 IO 太贵),这是”明明有索引却不用”的常见原因之一。

聚簇键选择原则

  • 首选自增/单调递增键:插入总在尾页,无页分裂、无随机 IO、页填充率高
  • 避免随机键(UUID/哈希)做聚簇键:页分裂频繁、碎片高、二级索引也变贵(二级索引叶存聚簇键,键变大=二级索引变大)
  • 避免过宽聚簇键:二级索引叶节点都带聚簇键副本,宽键放大所有二级索引
  • 避免频繁更新聚簇键:更新聚簇键=行迁移(整行搬页)
  • 无合适业务键时:无意义自增列做聚簇键( surrogate key ),业务唯一性用唯一索引保证

InnoDB 的默认行为链

有主键 → 主键即聚簇键;无主键 → 选第一个非空唯一索引;都没有 → InnoDB 生成隐藏 6 字节 ROW_ID 做聚簇键。**”每表必有聚簇索引”是 InnoDB 的组织前提**(索引组织表 IOT),与 MyISAM 的堆表+索引文件模式根本不同。

区别速记表

维度 聚簇索引 非聚簇索引
数量 每表一个 多个
叶节点 整行数据 索引键+聚簇键
决定 行的物理组织 独立副本树
范围扫描 顺序 IO 快 需回表时随机 IO
插入代价 受键序影响(页分裂) 每棵二级树各自维护
选型要害 单调、窄、不更新 按查询负载建、争取覆盖

聚簇与非聚簇的区别,一句话:一个决定行住哪,一个只是行的目录;目录查完要不要”回住处取东西”(回表),决定了二级索引查询的真实成本。抓住”叶节点存什么”这一个差异,其余特征与优化手段全部可推。