news 2026/8/25 16:24:24

MySQL InnoDB 引擎中的聚簇索引和非聚簇索引有什么区别?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL InnoDB 引擎中的聚簇索引和非聚簇索引有什么区别?

聚簇索引非聚簇索引的根本区别在于:数据存储方式和物理顺序


一、核心概念

1. 聚簇索引(Clustered Index)

聚簇索引是指索引的叶子节点直接存储了整行数据。在 InnoDB 中,主键就是聚簇索引

2. 非聚簇索引(Non-Clustered Index)

非聚簇索引是指索引的叶子节点存储的是主键值,而不是完整数据。需要根据主键值回表查询完整数据。也叫二级索引


二、结构对比图

1. 聚簇索引结构

聚簇索引(主键索引) ┌─────────────────────────────────┐ │ 根节点 │ │ [1-100] [101-200] [201-300] │ └────────────┬────────────────────┘ │ ┌───────┴───────┐ ▼ ▼ ┌─────────────┐ ┌─────────────┐ │ 叶子节点 │ │ 叶子节点 │ │ id=1 完整行 │ │ id=101 完整行│ │ id=2 完整行 │ │ id=102 完整行│ │ id=3 完整行 │ │ id=103 完整行│ │ ... │ │ ... │ └─────────────┘ └─────────────┘ 数据即索引 索引即数据

2. 非聚簇索引结构

非聚簇索引(如 name 索引) ┌─────────────────────────────────┐ │ 根节点 │ │ [A-F] [G-M] [N-Z] │ └────────────┬────────────────────┘ │ ┌───────┴───────┐ ▼ ▼ ┌─────────────┐ ┌─────────────┐ │ 叶子节点 │ │ 叶子节点 │ │ 'Alice' → 1 │ │ 'Bob' → 3 │ │ 'Ann' → 2 │ │ 'Ben' → 4 │ └─────────────┘ └─────────────┘ 存储主键值 需要回表查询

三、详细区别对比表

维度聚簇索引非聚簇索引
数据存储叶子节点存整行数据叶子节点存主键值
每张表数量只能有一个可以有多个
物理顺序数据按索引顺序存储数据独立存储
查询速度极快(一次查找)需要回表(两次查找)
占用空间无额外空间(数据本身)需要额外存储空间
主键选择强烈建议使用自增主键任何字段都可以建
插入性能顺序插入极快随机插入可能慢

四、工作原理示例

1. 建表和数据

CREATETABLEusers(idINTPRIMARYKEY,-- 聚簇索引nameVARCHAR(50),ageINT,emailVARCHAR(100),INDEXidx_name(name),-- 非聚簇索引INDEXidx_age(age)-- 非聚簇索引);INSERTINTOusersVALUES(1,'张三',25,' '),(2,'李四',30,' '),(3,'王五',28,' '),(4,'赵六',32,' ');

2. 通过聚簇索引查询

-- 通过主键查询(一次查找)SELECT*FROMusersWHEREid=3;-- 执行过程:-- 1. 在聚簇索引树中查找 id=3-- 2. 直接在叶子节点找到完整数据-- 3. 返回结果(不需要回表)-- 性能:极快,O(log n)

3. 通过非聚簇索引查询

-- 通过 name 索引查询(两次查找)SELECT*FROMusersWHEREname='王五';-- 执行过程:-- 1. 在 idx_name 索引树中找到 '王五'-- 2. 叶子节点存的是主键值:3-- 3. 拿着 id=3 回聚簇索引查询完整数据-- 4. 返回结果-- 性能:需要两次 B+Tree 查找-- 这叫:回表查询

五、回表查询的代价

1. 什么是回表?

-- 场景:查询所有字段EXPLAINSELECT*FROMusersWHEREname='王五';-- Extra 字段可能显示:Using where-- 执行计划显示需要回表

2. 如何避免回表?

-- 创建覆盖索引(索引包含所有需要的字段)CREATEINDEXidx_name_ageONusers(name,age);-- 查询只返回索引中的字段SELECTname,ageFROMusersWHEREname='王五';-- Extra: Using index(不需要回表!)-- 这种叫做:覆盖索引查询

六、主键选择对性能的影响

1. 使用自增主键(推荐)

CREATETABLEusers_autoinc(idINTPRIMARYKEYAUTO_INCREMENT,-- 顺序插入nameVARCHAR(50));-- 插入数据INSERTINTOusers_autoinc(name)VALUES('张三'),('李四'),('王五');-- 数据物理存储顺序:-- id: 1,2,3,4,5...(连续有序)-- 优点:-- 1. 插入快(只在最后追加)-- 2. 页分裂少-- 3. 空间利用率高

2. 使用 UUID 作主键(不推荐)

CREATETABLEusers_uuid(idVARCHAR(36)PRIMARYKEY,-- UUID 无序nameVARCHAR(50));-- 插入数据INSERTINTOusers_uuidVALUES(UUID(),'张三'),(UUID(),'李四');-- 数据物理存储顺序:-- id: 随机分散-- 缺点:-- 1. 插入慢(需要不断调整位置)-- 2. 频繁页分裂-- 3. 空间碎片多-- 4. 索引体积大

3. 性能对比

-- 自增主键插入:100万条/分钟-- UUID主键插入:30万条/分钟-- 差距:3-5倍!

七、聚簇索引的其他特点

1. 页合并和页分裂

-- 页分裂场景(非顺序插入)-- 当页满时,需要将一部分数据移到新页-- 影响插入性能-- 页合并场景(删除数据)-- 当页数据少于一半时,可能合并-- 优化空间使用

2. 辅助索引的叶子节点

-- InnoDB 辅助索引的叶子节点-- 存储的是主键值,不是行指针-- 优点:-- 1. 主键更新时不需要改辅助索引(但很少更新主键)-- 2. 辅助索引大小固定-- 缺点:-- 1. 需要回表查询-- 2. 占用更多空间

八、实际优化案例

案例1:查询优化

-- 原查询(需要回表)SELECTid,name,ageFROMusersWHEREageBETWEEN20AND30;-- 创建覆盖索引CREATEINDEXidx_ageONusers(age,name,id);-- 现在查询:SELECTage,name,idFROMusersWHEREageBETWEEN20AND30;-- Extra: Using index(不回表)

案例2:分页优化

-- 深分页问题SELECT*FROMusersORDERBYidLIMIT100000,10;-- 需要扫描 100010 行-- 优化:先查主键,再关联SELECT*FROMusers t1INNERJOIN(SELECTidFROMusersORDERBYidLIMIT100000,10)t2ONt1.id=t2.id;-- 二级索引扫描主键,减少回表

九、总结

核心区别

维度聚簇索引非聚簇索引
数量1个N个
存储内容完整数据主键值
查询次数1次2次(可能回表)
物理顺序按索引顺序独立存储
主键影响直接影响性能间接影响

选择建议

  1. 主键一定要用自增:避免页分裂,提高插入性能
  2. 查询尽量用覆盖索引:减少回表
  3. 避免 SELECT *:只查需要的字段
  4. 复合索引设计:考虑查询顺序

一句话理解

聚簇索引就像书的正文,本身已经按页码排好;非聚簇索引就像书的目录,告诉你某个关键词在哪些页码,要看到内容还得翻到对应页。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/25 16:18:53

基于本地大模型的Markdown转LaTeX自动化方案:从原理到工程实践

这次我们来看一个非常实用的本地大模型应用场景:将 Markdown 文档自动转换为 LaTeX 源码。对于需要撰写学术论文、技术报告或书籍的作者来说,在 Markdown 的便捷书写和 LaTeX 的精美排版之间反复手动转换,是一项耗时且容易出错的工作。借助本…

作者头像 李华
网站建设 2026/8/25 16:00:35

指针(4):C 语言数组与指针深度剖析

指针(4):C 语言数组与指针深度剖析前言1. 数组名1.1 数组名的理解1.2 例外1.2.1 sizeof(数组名)1.2.2 &数组名1.2.3 总结2.使用指针访问数组3.一维数组传参本质总结前言 很多人学 C 语言,数组和指针总…

作者头像 李华
网站建设 2026/8/25 15:54:59

Spark大数据分析与实战笔记(第九章 综合案例—Spark实时交易数据统计-02)

文章目录每日一句正能量第9章 综合案例—Spark实时交易数据统计章节概要9.3 模块开发—构建工程结构9.4 模块开发—构建订单系统9.4.1 模拟订单数据9.4.2 向Kafka集群发送订单数据9.5 模块开发 — 分析订单数据每日一句正能量 活在自己的热爱里,而不是别人的眼光里。…

作者头像 李华
网站建设 2026/8/25 15:44:37

rocketMQ proxy 延迟队列

Proxy 本身不做延迟队列的存储和调度 (那是 Broker 端 ScheduleMessageService / TimerMessageStore 的职责),Proxy 只负责 延迟消息的「发送端属性填充 延迟级别换算」以及消费端识别 。相关实现集中在 gRPC 发送链路和配置里。 发送端&…

作者头像 李华