Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > MySQL索引详解

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

作者:23516

MySQL索引的建立对于MySQL的高效运行是很重要的,索引可以大大提高MySQL的检索速度,这篇文章主要介绍了MySQL索引的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

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

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

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

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

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

1. 哈希表

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

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

2. 跳表

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

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

3. 二叉排序树

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

4. 平衡二叉树

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

5. 红黑树

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

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

6. B树

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

7. B+树

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

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

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

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

1. 聚簇索引

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

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

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

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

联合索引是针对多个字段创建的索引(如(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 在右边)。

注意,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,定位需要优化的查询。
  2. 执行计划(EXPLAIN):在SQL前加EXPLAIN,通过type(访问类型)、key(使用的索引)等字段判断是否走索引。

到此这篇关于MySQL索引的文章就介绍到这了,更多相关MySQL索引详解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文