聚集索引与非聚集索引的区别
- 聚集索引一个表只能有一个,而非聚集索引一个表可以存在多个
- 聚集索引存储记录是物理上连续存在,而非聚集索引是逻辑上的连续,物理存储并不连续
- 聚集索引:物理存储按照索引排序;聚集索引是一种索引组织形式,索引的键值逻辑顺序决定了表数据行的物理存储顺序
- 非聚集索引:物理存储不按照索引排序;非聚集索引则就是普通索引了,仅仅只是对数据列创建相应的索引,不影响整个表的物理存储顺序.
- 索引是通过二叉树的数据结构来描述的,我们可以这么理解聚簇索引:索引的叶节点就是数据节点。而非聚簇索引的叶节点仍然是索引节点,只不过有一个指针指向对应的数据块。
五条特征准确,但还差三块把区别变成可用知识:回表与覆盖索引的代价模型、聚簇键的选择原则、以及”物理连续”的准确含义(页内连续而非物理相邻)。本文补全。
结构再确认:叶节点存什么
- 聚簇索引(clustered):叶节点 = 完整行数据;表数据按聚簇键组织成一棵 B+Tree——“表即索引”
- 非聚簇索引(secondary):叶节点 = 索引键 + 聚簇键值(InnoDB);是独立于表数据的第二棵树
一个表只能有一个聚簇索引的原因由此自明:行的物理组织方式只能有一种;而二级索引可以有多个(每种键序一棵树)。
“物理连续”的准确含义
聚簇索引的”连续”指聚簇键顺序上的页内/逻辑连续(页间以指针相连、尽量Extent 顺序分配),而非磁盘物理扇区相邻。它带来的真实收益是:按聚簇键的范围扫描是顺序 IO;代价是:聚簇键无序插入(如 UUID)导致页分裂与随机 IO——这是聚簇键选型的全部要害。
回表与覆盖索引:代价模型
二级索引查询的两种结局:
- 查询列 ⊆ 二级索引列(含聚簇键)→ 覆盖索引,叶节点直接取数,一次树下行
- 否则 → 回表:拿叶中聚簇键再走一次聚簇树(第二次树下行,且是随机 IO)
-- idx_age 二级索引 |
优化动作:把高频查询列加入联合索引实现覆盖(EXPLAIN Extra: Using index 验证);回表行数大时优化器可能弃用二级索引改全表扫(随机 IO 太贵),这是”明明有索引却不用”的常见原因之一。
聚簇键选择原则
- 首选自增/单调递增键:插入总在尾页,无页分裂、无随机 IO、页填充率高
- 避免随机键(UUID/哈希)做聚簇键:页分裂频繁、碎片高、二级索引也变贵(二级索引叶存聚簇键,键变大=二级索引变大)
- 避免过宽聚簇键:二级索引叶节点都带聚簇键副本,宽键放大所有二级索引
- 避免频繁更新聚簇键:更新聚簇键=行迁移(整行搬页)
- 无合适业务键时:无意义自增列做聚簇键( surrogate key ),业务唯一性用唯一索引保证
InnoDB 的默认行为链
有主键 → 主键即聚簇键;无主键 → 选第一个非空唯一索引;都没有 → InnoDB 生成隐藏 6 字节 ROW_ID 做聚簇键。**”每表必有聚簇索引”是 InnoDB 的组织前提**(索引组织表 IOT),与 MyISAM 的堆表+索引文件模式根本不同。
区别速记表
| 维度 | 聚簇索引 | 非聚簇索引 |
|---|---|---|
| 数量 | 每表一个 | 多个 |
| 叶节点 | 整行数据 | 索引键+聚簇键 |
| 决定 | 行的物理组织 | 独立副本树 |
| 范围扫描 | 顺序 IO 快 | 需回表时随机 IO |
| 插入代价 | 受键序影响(页分裂) | 每棵二级树各自维护 |
| 选型要害 | 单调、窄、不更新 | 按查询负载建、争取覆盖 |
聚簇与非聚簇的区别,一句话:一个决定行住哪,一个只是行的目录;目录查完要不要”回住处取东西”(回表),决定了二级索引查询的真实成本。抓住”叶节点存什么”这一个差异,其余特征与优化手段全部可推。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 非鱼小站!
评论
WalineDisqus











