MySQL索引失效的原因及实现逻辑

 更新时间:2025年12月23日 08:22:18   作者:321茄子  
在MySQL中索引失效指的是查询语句无法有效地使用索引,而必须进行全表扫描,索引失效可能会导致查询性能下降,特别是在处理大量数据时,这篇文章主要介绍了MySQL索引失效的原因及实现逻辑,需要的朋友可以参考下

前言

在工作中,为了提高查询速度,我们通常会考虑为字段建立索引。然而,索引并不是万无一失的。即使建立了索引,并不意味着所有查询语句都能利用索引进行扫描。索引失效的原因如下

一、索引存储结构

MySQL 的默认存储引擎为 InnoDB,它使用 B+ 树作为索引的数据结构。选择 B+ 树作为索引结构的原因在于其高效的查找和插入性能。在创建表时,InnoDB 存储引擎会默认创建主键索引,即聚簇索引,而其他的索引则为二级索引。与 InnoDB 不同,MVISAM 存储引擎支持多种索引数据结构,包括 B+ 树索引、R 树索引和全文索引。在 MyISAM 中,创建表时默认使用 B+ 树作为主键索引。尽管 InnoDB 和 MyISAM 都支持 B+ 树索引,但它们的数据存储结构实现不同,InnoDB:B+树索引的叶子节点直接保存数据本身;MyISAM:B+树索引的叶子节点保存数据的物理地址。

1.1 MyISAM

1.2 InnoDB

2.1 普通索引

如果将 name 字段设置为普通索引,那么这个二级索引长下图这样

二、索引实现逻辑

2.1 主键索引查询

当我们使用主键索引字段进行查询时,如果数据在聚簇索引的叶子节点上,查询则会在B+树中直接找到对应的节点并读取数据。例如:

// id字段为主键索引
SELECT *FROM t user WHERE id=1;

2.2 二级索引查询

使用二级索引字段查询时,若数据在聚簇索引的叶子节点上,查询需要检索两个B+树:

1.首先在二级索引的B+树中找到叶子节点并获取主键值;

2.然后使用主键值在聚簇索引的B+树中找到对应节点,读取数据。这个过程称为“回表”,例如:

// name字段为二级索引
SELECT id FROM t user WHERE name="林某”;

三、索引失效

3.1 对索引使用左或者左右模糊匹配

在使用 LIKE 关键字进行模糊匹配时,如 LIKE'%xx'或 LIKE'%xx%'都会导致索引失效,从而引发全表扫描。例如,以下查询用于查找名称以「林」结尾的用户:

在这个查询的执行计划中, type=ALL 表示全表扫描,表明末使用索引。相反,如果查询名称以「林」开头的用户:

执行计划中的 type=range 表示使用了索引,key=index name 则确认了实际使用了索引。

为什么使用 LIKE 进行左模糊匹配会导致无法使用索引呢?

这是因为B+树索引是根据索引值有序存储的,仅能支持前缀比较。

举个例子,假设我们查询以「林」为前缀的名称。查询过程如下:

a.比较首节点的索引值:若「林」的拼音在某些节点中 介于「陈」和「周」之间,则继续査找下一个节点。

b.依此类推,直到找到符合前缀的叶子节点,并读取相应数据。

如果查询条件是 LIKE'%林·,因无法确定从哪个索引值开始比较,导致必须进行全表扫描。

3.2.对索引使用函数

在使用MySQL时,我们常常依赖内置函数来获取所需的查询结果然而,需要特别注意的是,如果在查询条件中对索引字段使用了函数,这通常会导致索引失效,从而导致全表扫描,例如,以下查询语句中,length(name)函数被应用于 name 字段。

关键在于,索引存储的是字段的原始值,而不是经过函数处理后的计算结果,这使得数据库无法有效利用索引。

不过从MySQL8.0版本开始,数据库引入了函数索引这一特性,允许我们针对函数计算结果建立索引。也就是说,索引的值可以是某个函数计算后的结果,这样就可以通过索引来快速查询所需数据。

例如,我们可以使用以下语句创建一个名为 idx_name length 的索引,该索引对应于 length(name)的计算结果:

ALTER TABLE t user ADD KEY idx name length((LENGTH(name)));

在这种情况下,当我们使用类似的查询时,数据库将能够利用索引来加速查询过程。

3.3.对索引进行表达式计算

在查询条件中进行表达式计算通常会导致无法使用索引。举个例子,以下查询语句在执行计划中显示为类型ALL,表明使用了全表扫描:

然而,如果将查询条件修改为 WHERE id=10-1,则可以利用索引进行査询。

为什么表达式计算会使索引失效呢?

原因与对索引使用函数类似,索引中保存的是字段的原始值,而不是经过计算的结果,因此数据库必须先取出字段所有的值,然后逐个进行计算,从而导致全表扫描。

2.4.对索引隐式类型转换

如果索引字段是字符串类型,但是在条件查询中,输入的参数是整型的话,查看执行计划结果可以发现这条语句会走全表扫描。
在人员表中,我增加了一个 phone 字段,该字段为二级索引,类型为 VARCHAR。

在进行条件查询时,如果使用整型作为输入参数,如执行以下SQL:

然而,如果索引字段是整型,即使查询条件中使用字符串类型的参数,索引依然能够正常生效。例如,执行以下SQL:

在这种情况下,查询仍然会走索引扫描。

这引发了一个问题:为何第一个例子导致索引失效,而第二个例子却没有?

首先需要掌握MSQL的数据类型转换规则,它决定了在比较字符串和数字时,哪个会被转换。用一个简单的方法来测试这一规则:选择SELECT"10">9

如果MVSQL会自动将字符串转换为数字,那么这相当于执行 SELECT 10>9,结果应该是1,因为数字10确实大于9。如果MVSOL会将数字转换为字符串,那么这相当于执行 SELECT"1”>"9”。在这种情况下,字符串比较是逐位进行的,按照ASCI码进行比较。首先比较字符“1”和“9”,由于“1”的ASCII码小于“9”,所以结果应该是0。

根据测试结果,可以知道MySQL在比较时,会将字符串转换为数字。

因此第二个查询可以走索引,就是查不到期望正确的值。

2.5.联合索引非最左匹配

在数据库索引中,对主键字段建立的索引称为聚簇索引,而对普通字段建立的索引则称为二级索引。

当多个普通字段组合在一起创建索引时,称为联合索引(或组合索引)。

创建联合索引时,字段的顺序至关重要。例如,联合索引(a,b,c)与(c,b,a)在使用时会有显著不同。为了有效利用联合索引,必须遵循最左匹配原则,即查询条件必须从最左边的字段开始匹配。

例如,对于(a,b,c)的联合系引,以下查询能够成功匹配联合系引:

  • WHERE a=3。
  • WHERE a=3 AND b=5 AND C=4
  • WHERE a=3 AND b=5

然而,若查询条件不符合最左匹配原则,索引将失效,如以下查询

  • WHERE b=5
  • WHERE C=4
  • WHERE b=5 AND C=4

还有一种特殊情况是 WHERE a=3 AND c=4。特殊之处在于不同版本的 MySQL,处理方法有所不同。

  • 在 MySQL5.5 中,虽然会使用索引定位到主键,但仍需回表读取数据。
  • 而自 MVSQL 5.6 以来,引入的索引下推功能使得条件判断在存储引擎层进行,从而减少了回表次数,提高了性能。

例如,通过执行计划中的 Extra=Using index condition 可以确认索引下推的使用。

为什么联合索引不遵循最左匹配原则就会失效?

联合索引内部的排序是基于第一列。只有当第一列的数据相同,才会依据第二列排序。因此,若希望利用联合索引中尽可能多的列,查询条件中的各列必须从最左侧开始连续匹配。

2.6.WHERE 子句中的 OR

如果在 WHERE 子句中,0R 前的条件列是索引列,而 0R 后的条件列不是索引列,将会面临全表扫描的问题。举个例子,考虑以下查询语句:

//id 是主键且为索引列,age 是普通列
SELECT*FRoM tuser WHERE id=1OR age = 18;

执行计划显示,该查询会执行全表扫描,因为0R的逻辑是,只需满足任一条件即可。

因此,当有一个条件不使用索引时,索引的存在毫无意义。

解决这个问题很简单:将 age 字段设置为索引。这样,查询将会充分利用索引,避免全表扫描。

ALTER TABLE t user ADD INDEX idx age(age);

优化后,如果执行计划变为“type=index merge“,则意味着数据库分别对 id 和 age 条件进行了索引扫描,并将结果合并,从而提高了查询效率。

通过这种方式,可以显著提升查询性能,减少数据库的负载。

总结

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

相关文章

  • MySQL数据库防止人为误操作的实例讲解

    MySQL数据库防止人为误操作的实例讲解

    这篇文章主要介绍了MySQL数据库防止人为误操作的方法,需要的朋友可以参考下
    2014-06-06
  • MySql实现分布式锁详解

    MySql实现分布式锁详解

    这篇文章主要为大家详细介绍了如何使用本地MySql实现一把分布式锁,以及Mysql实现分布式锁的原理是怎么样的,有需要的小伙伴可以了解下
    2024-11-11
  • MySql死锁排查的问题解决

    MySql死锁排查的问题解决

    本文主要介绍了MySQL死锁的排查方法和解决方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-12-12
  • mysql出现ERROR问题:(2006, ‘MySQL server has gone away‘)

    mysql出现ERROR问题:(2006, ‘MySQL server has gone away‘)

    这篇文章主要介绍了mysql出现ERROR问题:(2006, ‘MySQL server has gone away‘),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-09-09
  • MySQL中的binlog相关命令和恢复技巧

    MySQL中的binlog相关命令和恢复技巧

    这篇文章主要介绍了MySQL中的binlog相关命令和恢复技巧,需要的朋友可以参考下
    2014-05-05
  • MySQL 事务与锁机制详解及注意事项

    MySQL 事务与锁机制详解及注意事项

    MySQL 的事务与锁机制共同构成了数据库并发控制的核心,通过遵循 ACID 原则和合理设置事务隔离级别,可以有效地保障数据的一致性和完整性,这篇文章主要介绍了MySQL 事务与锁机制详解,需要的朋友可以参考下
    2025-04-04
  • MySQL用户账户管理和权限管理深入讲解

    MySQL用户账户管理和权限管理深入讲解

    这篇文章主要给大家介绍了关于MySQL用户账户管理和权限管理的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-12-12
  • MySQL多表查询的具体实例

    MySQL多表查询的具体实例

    这篇文章主要介绍了MySQL多表查询的具体实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • 详解MySQL主从复制实战 - 基于GTID的复制

    详解MySQL主从复制实战 - 基于GTID的复制

    本篇文章主要介绍了MySQL主从复制实战 - 基于GTID的复制,基于GTID的复制是MySQL 5.6后新增的复制方式.有兴趣的可以了解一下。
    2017-03-03
  • 浅谈MySql update会锁定哪些范围的数据

    浅谈MySql update会锁定哪些范围的数据

    本文主要介绍了记录一下MySql update会锁定哪些范围的数据,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06

最新评论