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+树中直接找到对应的节点并读取数据。例如:

sql 复制代码
// id字段为主键索引
SELECT *FROM t user WHERE id=1;

2.2 二级索引查询

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

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

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

sql 复制代码
// 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)的计算结果:

sql 复制代码
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 后的条件列不是索引列,将会面临全表扫描的问题。举个例子,考虑以下查询语句:

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

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

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

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

sql 复制代码
ALTER TABLE t user ADD INDEX idx age(age);

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

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

相关推荐
weixin_4365250712 分钟前
jar包启动使用logs替换nohup日志文件
java·linux·数据库
7***998713 分钟前
Redis——Windows安装
数据库·windows·redis
Elastic 中国社区官方博客20 分钟前
Elasticsearch:在隔离环境中安装 ELSER 模型
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
v***79426 分钟前
mysql配置环境变量——(‘mysql‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件解决办法)
数据库·mysql·adb
散修-小胖子29 分钟前
TPCC-MySQL快速上手
数据库·mysql·oracle
roman_日积跬步-终至千里30 分钟前
【模式识别与机器学习(18)】关联规则深入浅出教程
数据库·机器学习·oracle
杨DaB32 分钟前
【MySQL】06 视图 view
数据库·mysql
spencer_tseng35 分钟前
MySQL my.cnf
mysql
星空露珠40 分钟前
lua获取随机颜色rgb转换hex
数据结构·数据库·算法·游戏·lua