听说mysql还会选错索引

2023-10-28 14:08

本文主要是介绍听说mysql还会选错索引,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

大家都知道,mysql 一个表中可以创建多个索引,但是在执行一条查询语句的时候,mysql 只能选一个索引,如果我们没有指定 mysql 使用某个索引,那么就是由 mysql 的优化器来决定要使用哪个索引了,然而,mysql 也是会有选错的时候。

 

前面的文章,我们有介绍过执行一条查询 sql 语句分别会经历那些过程,执行一条sql语句都经历了什么? 存在多个索引的情况下,优化器一般会通过比较扫描行数、是否需要临时表以及是否需要排序等,来作为选择索引的判断依据。

 

我们先来新建一个表,创建两个普通索引。

 

CREATE TABLE `t` (`id` int(11) NOT NULL,`a` int(11) DEFAULT NULL,`b` int(11) DEFAULT NULL,PRIMARY KEY (`id`),KEY `a` (`a`),KEY `b` (`b`)
) ENGINE=InnoDB;

 

这里我们使用存储过程往表里插入 10w 测试数据,如果对 mysql 的存储过程不熟悉,请看我在代码中的注释,应该能看得懂得。

 

#定义分割符号,mysql 默认分割符为分号;,这里定义为 //
#分隔符的作用主要是告诉mysql遇到下一个 // 符号即执行上面这一整段sql语句
delimiter //#创建一个存储过程,并命名为 testData
create procedure testData() #下面这段就是表示循环往表里插入10w条数据
begindeclare i int;set i=1;while(i<=100000)doinsert into t values(i, i, i);set i=i+1;end while;
end //  #这里遇到//符号,即执行上面一整段sql语句delimiter ; #恢复mysql分隔符为;call testData(); #调用存储过程

 

数据插入完成后,我们来看下面这条 sql 语句。

 

select * from t where (a between 1 and 1000) and (b between 50000 and 100000) order by b limit 1;

 

由于主键id、a、b 三个字段的值其实都是一样的,所以其实这条 sql 语句的结果集为空,没有符合条件的记录。

 

我们来看看 mysql 该是怎么选择索引的,这里有三个索引可用,分别是主键索引、索引a、索引b。

 

如果选择主键索引虽然可以减少回表过程,但是只能走全表扫描,需要扫描 10w 条记录。

 

如果选择索引 a,则只需在 a 索引上扫描 1k 条记录,然后回到主键索引上过滤掉不满足 b 条件的记录,最后再按 b 排序即可。

 

如果选择索引 b,则需要在 b 索引上扫描 5w 条记录,然后同样回到主键索引上过滤掉不满足 a 条件的记录,因为索引有序,所以使用 b 索引不需要额外排序。

 

我们来使用执行计划看下 mysql 究竟会选择哪个索引。

 

mysql> explain select * from t where (a between 1 and 1000) and (b between 50000 and 100000) order by b limit 1;
+----+-------------+-------+-------+---------------+------+---------+------+-------+------------------------------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows  | Extra                              |
+----+-------------+-------+-------+---------------+------+---------+------+-------+------------------------------------+
|  1 | SIMPLE      | t     | range | a,b           | b    | 5       | NULL | 50128 | Using index condition; Using where |
+----+-------------+-------+-------+---------------+------+---------+------+-------+------------------------------------+
1 row in set (0.12 sec)

 

可以看出 mysql 是选择使用索引 b,虽然扫描行数要多一些,但因为索引本身是有序的,使用索引 b 可以避免排序,mysql 认为这个排序的代价高于扫描行数。

 

上面这个选择是 mysql 优化器内部的分析,那么实际情况又如何呢,我们可以分别执行一下 sql 语句,使用 force index(a) 强制使用索引 a 来对比下,看下两者具体花费的时间。

 

mysql> select * from t where (a between 1 and 1000) and (b between 50000 and 100000) order by b limit 1;
Empty set (0.65 sec)mysql> select * from t force index(a) where (a between 1 and 1000) and (b between 50000 and 100000) order by b limit 1;
Empty set (0.05 sec)

 

从结果可以看到其实使用索引 a 显然速度会更快,所以这就是属于 mysql 选错了索引的情况,那我们怎么避免这种情况呢,我们可以把 sql 语句改成下面这样的,即把 order by b 改成 order by b,a 。

 

select * from t where a between 1 and 1000 and b between 50000 and 100000 order by b,a limit 1;

 

这样的话,在 mysql 看来无论是使用索引 a 还是索引 b 都需要排序了,那就只能选择扫描行更少的索引了,所以 mysql 会选择索引 a,从而达到避免 mysql 选错索引的目的,我们可以看下优化后的这条 sql 的执行计划。

 

mysql> explain select * from t where a between 1 and 1000 and b between 50000 and 100000 order by b,a limit 1;
+----+-------------+-------+-------+---------------+------+---------+------+------+----------------------------------------------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra                                              |
+----+-------------+-------+-------+---------------+------+---------+------+------+----------------------------------------------------+
|  1 | SIMPLE      | t     | range | a,b           | a    | 5       | NULL |  999 | Using index condition; Using where; Using filesort |
+----+-------------+-------+-------+---------------+------+---------+------+------+----------------------------------------------------+
1 row in set (0.00 sec)

 

大多数情况下,mysql 都会选择正确的索引,选错索引算是比较少见的特殊情况了,文中的例子也是个特例,仅是给大家提供一个分析思路,当你遇到一些已经使用了索引但依然比较慢的 sql 语句的时候,可以尝试分析是否是 mysql 选错了索引的原因。

 

其实还有一些情况,会导致 mysql 选错索引,就是 mysql 预估扫描行的数据不够准确,而这个不准确通常是数据表有频繁的删除或更新操作导致的数据空洞造成的,关于这个原因,我会在后面再详细讲。

 

这篇文章如果对你有些启发,不妨点个赞吧,感谢支持,当然如果对文中有不太明白的地方,欢迎留言。另外,不知道大家对 explain 这个命令熟悉不,如果不熟悉的话,我考虑再单独写一篇关于 explain 使用的文章。

 

这篇关于听说mysql还会选错索引的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

oracle数据库索引失效的问题及解决

《oracle数据库索引失效的问题及解决》本文总结了在Oracle数据库中索引失效的一些常见场景,包括使用isnull、isnotnull、!=、、、函数处理、like前置%查询以及范围索引和等值索引... 目录oracle数据库索引失效问题场景环境索引失效情况及验证结论一结论二结论三结论四结论五总结ora

Mysql 中的多表连接和连接类型详解

《Mysql中的多表连接和连接类型详解》这篇文章详细介绍了MySQL中的多表连接及其各种类型,包括内连接、左连接、右连接、全外连接、自连接和交叉连接,通过这些连接方式,可以将分散在不同表中的相关数据... 目录什么是多表连接?1. 内连接(INNER JOIN)2. 左连接(LEFT JOIN 或 LEFT

mysql重置root密码的完整步骤(适用于5.7和8.0)

《mysql重置root密码的完整步骤(适用于5.7和8.0)》:本文主要介绍mysql重置root密码的完整步骤,文中描述了如何停止MySQL服务、以管理员身份打开命令行、替换配置文件路径、修改... 目录第一步:先停止mysql服务,一定要停止!方式一:通过命令行关闭mysql服务方式二:通过服务项关闭

SQL Server数据库磁盘满了的解决办法

《SQLServer数据库磁盘满了的解决办法》系统再正常运行,我还在操作中,突然发现接口报错,后续所有接口都报错了,一查日志发现说是数据库磁盘满了,所以本文记录了SQLServer数据库磁盘满了的解... 目录问题解决方法删除数据库日志设置数据库日志大小问题今http://www.chinasem.cn天发

mysql主从及遇到的问题解决

《mysql主从及遇到的问题解决》本文详细介绍了如何使用Docker配置MySQL主从复制,首先创建了两个文件夹并分别配置了`my.cnf`文件,通过执行脚本启动容器并配置好主从关系,文中还提到了一些... 目录mysql主从及遇到问题解决遇到的问题说明总结mysql主从及遇到问题解决1.基于mysql

Python中列表的高级索引技巧分享

《Python中列表的高级索引技巧分享》列表是Python中最常用的数据结构之一,它允许你存储多个元素,并且可以通过索引来访问这些元素,本文将带你深入了解Python列表的高级索引技巧,希望对... 目录1.基本索引2.切片3.负数索引切片4.步长5.多维列表6.列表解析7.切片赋值8.删除元素9.反转列表

MySQL的索引失效的原因实例及解决方案

《MySQL的索引失效的原因实例及解决方案》这篇文章主要讨论了MySQL索引失效的常见原因及其解决方案,它涵盖了数据类型不匹配、隐式转换、函数或表达式、范围查询、LIKE查询、OR条件、全表扫描、索引... 目录1. 数据类型不匹配2. 隐式转换3. 函数或表达式4. 范围查询之后的列5. like 查询6

Linux下MySQL8.0.26安装教程

《Linux下MySQL8.0.26安装教程》文章详细介绍了如何在Linux系统上安装和配置MySQL,包括下载、解压、安装依赖、启动服务、获取默认密码、设置密码、支持远程登录以及创建表,感兴趣的朋友... 目录1.找到官网下载位置1.访问mysql存档2.下载社区版3.百度网盘中2.linux安装配置1.

PostgreSQL如何查询表结构和索引信息

《PostgreSQL如何查询表结构和索引信息》文章介绍了在PostgreSQL中查询表结构和索引信息的几种方法,包括使用`d`元命令、系统数据字典查询以及使用可视化工具DBeaver... 目录前言使用\d元命令查看表字段信息和索引信息通过系统数据字典查询表结构通过系统数据字典查询索引信息查询所有的表名可

PostgreSQL如何用psql运行SQL文件

《PostgreSQL如何用psql运行SQL文件》文章介绍了两种运行预写好的SQL文件的方式:首先连接数据库后执行,或者直接通过psql命令执行,需要注意的是,文件路径在Linux系统中应使用斜杠/... 目录PostgreSQ编程L用psql运行SQL文件方式一方式二总结PostgreSQL用psql运