一文详解MySQL索引(六张图彻底搞懂)

2025-09-23 12:50

本文主要是介绍一文详解MySQL索引(六张图彻底搞懂),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《一文详解MySQL索引(六张图彻底搞懂)》MySQL索引的建立对于MySQL的高效运行是很重要的,索引可以大大提高MySQL的检索速度,:本文主要介绍MySQL索引的相关资料,文中通过代码介绍的...

一、什么是索引?为什么需要索引?

查字典时,你会逐页翻找某个汉字吗?显然不会。我们通常会先查目录,通过拼音或部首定位到汉字所在的页码——这个目录就是一种索引。它通过额外的空间存储(目录页),换取了查询速度的大幅提升,这正是"空间换时间"的经典思想。

数据库中的索引本质相同:它是一种能快速定位数据的数据结构,核心作用是加速SQL查询。没有索引的查询就像逐页翻字典,需要扫描整张表(全表扫描),在数据量庞大时效率极低;而有了索引,数据库能直接定位到目标数据所python在的位置,避免无效扫描。

二、索引该用哪种数据结构?

mysql索引设计的出发点在于,要能按区间高效地范围查找,还要尽少在磁盘 I/O 操作中做查询

1. 哈希表

哈希表通过键值对存储,查询单个数据的时间复杂度是O(1),看似高效,但有致命缺陷:

  • 无法支持范围查询(如age > 30
  • 不能按顺序返回结果(无法满足ORDER BY需求)
  • 哈希冲突会导致性能波动

因此,哈希表仅适合精准匹配的场景(如字典查询),无法作为数据库的主力索引结构。

2. 跳表

跳表是一种基于链表的 “分层索引结构”,核心思路是给基础链表增加多级索引,实现类似 “二分查找” 的高效查询:

  • 结构特点:底层是有序链表,上层索引层按固定间隔(如每 2 个节点)抽取节点形成,最高层索引指向链表首尾,通过索引层快速定位范围,再下沉到底层链表精确查找。
  • 优势:支持范围查询(天然有序),插入 / 删除无需旋转(只需android调整索引层指针),实现复杂度低于平衡树。
  • 缺陷:随着数据量递增,索引层数会同步增加(百万级数据可能需要 10 + 层索引)。数据库索引需持久化到磁盘,每一层索引的访问都对应一次磁盘 IO—— 多层索引会导python致 IO 次数激增,反而比二叉树更低效。此外,跳表的索引层占用大量额外空间,数据量越大空间开销越突出。

跳表更适合内存数据库(如 Redis 的 Sorted Set),磁盘数据库中因 IO 开销问题极少采用。

一文详解MySQL索引(六张图彻底搞懂)

3. 二叉排序树

二叉排序树(左子树 < 根节点 < 右子树)支持范围查询和排序,但存在严重缺陷:

  • 顺序插入时会退化为链表(如插入1、2、3、4),查询时间复杂度从O(logn)暴跌至O(n)
  • 树高过高导致磁盘IO频繁(数据库索引需持久化到磁盘,树高直接影响IO次数)

一文详解MySQL索引(六张图彻底搞懂)

4. 平衡二叉树

平衡二叉树(如AVL树)通过旋转操作维持平衡(左右子树高度差≤1),解决了退化问题,但新问题出现:

  • 为保持"绝对平衡",插入/删除时需频繁旋转,导致大量磁盘IO
  • 仍为二叉结构(每个节点最多2个子树),数据量过大时树高依然很高(百万级数据树高约20)

5. 红黑树

红黑树是一种"近似平衡"的二叉树,通过变色和有限旋转维持平衡,不追求绝对高度差:

  • 减少了旋转次数,降低了插入/删除的IO开销
  • 但本质仍是二叉树,数据量庞大时树高问题依然存在(千万级数据树高约30)

红黑树更适合内存中的小规模数据(如Java HashMap中链表转红黑树的阈值为8),而非磁盘存储的数据库索引。

6. B树

一文详解MySQL索引(六张图彻底搞懂)

B树是多路平衡排序树(一个节点可包含多个子树),显著降低了树高:

  • 每个节点存储多个键值对(索引+数据),减少IO次数
  • 查询效率不稳定,如有的数据在二层有的数据在最后一层
  • 不方便范围查询:B 树能高效的通过等值查询 90 这个值,但不方便查询出一个期间内 3 ~ 10 区间内所有数的结果。因为当 B 树做范围查询时需要使用中序遍历,那么父节点和子节点也就需要不断的来回切换涉及了多个节点会给磁盘 I/O 带来很多负担。

7. B+树

B+树就完美解决了上述问题:

  • 非叶子节点只存索引:单个节点可容纳更多索引,树高显著降低(百万级数据树高通常≤3)
  • 数据只在叶子节点存储:所有叶子节点通过双向链表连接,范围查询只需遍历链表,无需回溯
  • 磁盘IO友好:树高低+顺序IO,大幅减少磁盘访问次数

因此,MySQL等主流数据库均采用B+树作为索引的底层数据结构。

三、B+树是如何存索引的

MySQL的索引按存储方式可分为两类,核心区别在于"索引是否与数据存放在一起":

1. 聚簇索引

  • 特点:索引与数据存储在一起,叶子节点包含完整的行数据
  • 典型案例:主键索引(InnoDB表必有的索引)
  • 优势:查询主键时无需额外操作,直接获取数据
  • 注意点:主键应设计为自增字段。若主键无序(如UUID),插入时会导致B+树频繁"页分裂",严重影响性能

一文详解MySQL索引(六张图彻底搞懂)

2. 非聚簇索引(二级索引)

  • 特点:索引与数据分离,叶子节点存储索引值+主键ID

  • 典型案例:唯一索引、普通索引、前缀索引等

  • 查询流程:需经过"回表"操作——先通过二级索引找到主键ID,再通过主键索引查询完整数据

查二级索引一定会回表吗?

不一定,如果只查id,那二级索引的叶子结点就有id;如果查询字段均可在二级索引中找到,也无需回表,这就是覆盖索引js

一文详解MySQL索引(六张图彻底搞懂)

四、联合索引与最左前缀匹配法则

联合索引是针对多个字段创建的索引(如(name, age, gender)),其B+树按字段顺序排序(先按name,再按age,最后按gender)。使用时需遵循最左前缀匹配法则

  1. 必须从左到右匹配

    • 有效:WHERE name='张三'WHERE name='张三' AND age=20
    • 无效:WHERE age=20(跳过了最左的name)、WHERE name='张三' AND gender='男'(跳过了中间的age)
  2. 字段顺序不影响有效性

    • WHERE age=20 AND name='张三' 会被MySQL优化器调整为name='张三' AND age=20,仍能使用索引
  3. 范围查询会中断匹配

    • WHERE name='张三' AND age>20 AND gender='男' 中,age>20是范围查询,后续的gender无法使用索引

为什么不从最左开始查,就无法匹配呢

比如有一个 user 表,我们给 name 和 age 建立了一个联合索引 (name, age)

ALTER TABLE user add INDEX comidx_name_phone (name,age);

联合索引在 B+ 树中是复合的数据结构,按照从左到右的顺序依次建立搜索树 (name 在左边,age 在右边)。

一文详解MySQL索引(六张图彻底搞懂)

注意,name 是有序的,age 是无序的。当 name 相等的时候,age 才有序。

五、索引失效的场景

理解索引失效的原因,本质是理解B+树的查询逻辑:

  1. 模糊查询前缀含通配符

    • 有效:name LIKE '张%'(前缀明确,可匹配索引)
    • 无效:name LIKE '%张'(前缀模糊,无法定位索引位置)
  2. 索引列参与运算或函数

    • 无效:WHERE YEAR(birthday) = 1990WHERE age + 1 = 30(索引存储原始值,运算后无法匹配)
  3. 隐式类型转换

    • 无效:WHERE phone = 13800138000(若phone是字符串类型,会触发CAST(phone AS UNSIGNED),等价于函数操作)
  4. OR条件包含非索引列

    • 无效:WHERE name='张三' OR address='北京'(address无索引时,无法同时走索引和全表扫描,直接退化为全表扫描)

六、索引设计的最佳实践

索引并非越多越好,需在查询性能与写入性能间平衡:

  1. 适合建索引的场景

    • 数据量大且查询频繁的表
    • WHEREGROUP BYORDER BY涉及的字段
    • 区分度高的字段(如身份证号,而非性别)
  2. 不适合建索引的场景

    • 增删改频繁的列(索引会增加写入开销)
    • 数据量极小的表(全表扫描可能更快)
    • 区分度低的字段(如性别、状态),整个b+树一边男一边女,加索引也提高不了效率
  3. 实用技巧

    • 优先使用联合索引,提高覆盖索引概率,避免回表
    • 长字符串用前缀索引(如name(10)),减少空间占用
    • 控制单表索引数量(建议≤5个)
    • 避免SELECT *,减少回表操作

七、如何分析索引使用情况?

  1. 慢查询日志:开启slow_query_log,记录执行时间超过阈值的SQL,定位需要优化的查询js
  2. 执行计划(EXPLAIN):在SQL前加EXPLAIN,通过type(访问类型)、key(使用的索引)等字段判断是否走索引。

到此这篇关于MySQL索引的文章就介绍到这了,更多相关MySQL索引详解内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程China编程(www.chinasem.cn)!

这篇关于一文详解MySQL索引(六张图彻底搞懂)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1155984

相关文章

python语言中的常用容器(集合)示例详解

《python语言中的常用容器(集合)示例详解》Python集合是一种无序且不重复的数据容器,它可以存储任意类型的对象,包括数字、字符串、元组等,下面:本文主要介绍python语言中常用容器(集合... 目录1.核心内置容器1. 列表2. 元组3. 集合4. 冻结集合5. 字典2.collections模块

MySQL批量替换数据库字符集的实用方法(附详细代码)

《MySQL批量替换数据库字符集的实用方法(附详细代码)》当需要修改数据库编码和字符集时,通常需要对其下属的所有表及表中所有字段进行修改,下面:本文主要介绍MySQL批量替换数据库字符集的实用方法... 目录前言为什么要批量修改字符集?整体脚本脚本逻辑解析1. 设置目标参数2. 生成修改表默认字符集的语句3

详解Spring中REQUIRED事务的回滚机制详解

《详解Spring中REQUIRED事务的回滚机制详解》在Spring的事务管理中,REQUIRED是最常用也是默认的事务传播属性,本文就来详细的介绍一下Spring中REQUIRED事务的回滚机制,... 目录1. REQUIRED 的定义2. REQUIRED 下的回滚机制2.1 异常触发回滚2.2 回

linux配置podman阿里云容器镜像加速器详解

《linux配置podman阿里云容器镜像加速器详解》本文指导如何配置Podman使用阿里云容器镜像加速器:登录阿里云获取专属加速地址,修改Podman配置文件并移除https://前缀,最后拉取镜像... 目录1.下载podman2.获取阿里云个人容器镜像加速器地址3.更改podman配置文件4.使用po

使用Go调用第三方API的方法详解

《使用Go调用第三方API的方法详解》在现代应用开发中,调用第三方API是非常常见的场景,比如获取天气预报、翻译文本、发送短信等,Go作为一门高效并发的编程语言,拥有强大的标准库和丰富的第三方库,可以... 目录引言一、准备工作二、案例1:调用天气查询 API1. 注册并获取 API Key2. 代码实现3

MySQL8.0临时表空间的使用及解读

《MySQL8.0临时表空间的使用及解读》MySQL8.0+引入会话级(temp_N.ibt)和全局(ibtmp1)InnoDB临时表空间,用于存储临时数据及事务日志,自动创建与回收,重启释放,管理高... 目录一、核心概念:为什么需要“临时表空间”?二、InnoDB 临时表空间的两种类型1. 会话级临时表

MySQL之复合查询使用及说明

《MySQL之复合查询使用及说明》文章讲解了SQL复合查询中emp、dept、salgrade三张表的使用,涵盖多表连接、自连接、子查询(单行/多行/多列)及合并查询(UNION/UNIONALL)等... 目录复合查询基本查询回顾多表查询笛卡尔积自连接子查询单行子查询多行子查询多列子查询在from子句中使

Kotlin 协程之Channel的概念和基本使用详解

《Kotlin协程之Channel的概念和基本使用详解》文章介绍协程在复杂场景中使用Channel进行数据传递与控制,涵盖创建参数、缓冲策略、操作方式及异常处理,适用于持续数据流、多协程协作等,需注... 目录前言launch / async 适合的场景Channel 的概念和基本使用概念Channel 的

MySQL使用EXISTS检查记录是否存在的详细过程

《MySQL使用EXISTS检查记录是否存在的详细过程》EXISTS是SQL中用于检查子查询是否返回至少一条记录的运算符,它通常用于测试是否存在满足特定条件的记录,从而在主查询中进行相应操作,本文给大... 目录基本语法示例数据库和表结构1. 使用 EXISTS 在 SELECT 语句中2. 使用 EXIS

Android实现图片浏览功能的示例详解(附带源码)

《Android实现图片浏览功能的示例详解(附带源码)》在许多应用中,都需要展示图片并支持用户进行浏览,本文主要为大家介绍了如何通过Android实现图片浏览功能,感兴趣的小伙伴可以跟随小编一起学习一... 目录一、项目背景详细介绍二、项目需求详细介绍三、相关技术详细介绍四、实现思路详细介绍五、完整实现代码