浅谈MySQL为什么会选错索引

 更新时间:2023年03月20日 11:09:59   作者:XHHP  
本文主要介绍了浅谈MySQL为什么会选错索引,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

1.引例

首先创建一张表,并对字段a,b分别建立索引:

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;

然后往表中,插入十万行数据,值按整数递增:(1,1,1)、(2,2,2)、(3,3,3)…

delimiter ;;
create PROCEDURE insertdata()
begin 
	declare i int;
	set i=1;
	while(i<=100000) DO
		insert into t values(i,i,i);
		set i = i+1;
	end while;
end;;
delimiter ;
call insertdata();

接下来,我们执行一条sql:

mysql >explain select * from t where a between 10000 and 20000;

执行结果:

结果中的“key”字段就代表了查询中使用的索引。所以这条语句走了索引a,没什么问题。

我们再来执行如下操作:

但是这个时候session B的查询语句select * from t where a between 10000 and 20000就不会再选择索引a。

为了比较使用索引和不使用的查询性能对比,执行下面的语句:

set long_query_time=0;
select * from t where a between 10000 and 20000;
select * from t force(a) where a between 10000 and 20000;

下面是两种慢查询日志中的结果对比:

第一个查询查找了十万行,第二个查询走了索引,查找了一万行,速度明显比较快。

那为什么会选错索引呢?

2.优化器的逻辑

选择索引是优化器的工作,优化器选择索引的目的,就是想要找到一个最优的执行方案,并用最小的代价去执行。

在数据库里面,扫描行数是影响执行代价的因素之一。扫描行数越少,意味着访问磁盘次数越少。但是扫描行数并不是唯一的评价标准,还会考虑临时表,是否排序等因素。

那扫描行数是如何判断的?
MySQL在真正执行之前,只能根据统计信息来估算记录数。这个统计信息就是索引的“区分度”。 一个索引上不同的值越多,这个索引的区分度就越好。而一个索引上不同的值的个数,我们称之为“基数”(cardinality)。也就是说,这个基数越大,索引的区分度越好。

我们可以用show index的方法看到不同索引的基数值,但是可以看到统计信息并不是太准确。 可以使用analyze table t来重新统计,但是也不一定准确。

那MySQL是如何得到索引的基数呢?
答案是MySQL会采取采样统计的方法,默认会选择N个数据页,统计这些页面上的不同值,得到平均值,再乘以总的页面数。

在MySQL中,有两种存储索引统计的方式,可以通过设置innodb_stats_persisten来设置:

  • 设置为on的时候,表示统计信息会持久化存储。这时,默认的N是20,M是10
  • 设置为off的时候,表示统计信息只存储在内存中。这时,默认的N是8,M是16

我们再来比较两个语句预估的查询行数,如下图:

图中的row字段就代表预估的查询行数。对于第一条语句,预估的查询行数是104620.第二条语句,预估的查询行数是37116。明显第二条语句的查询行数少,那为什么没有选择索引a呢?

这是因为,如果使用索引a,每次从索引a上拿到一个值,都要回表查询。而如果选择扫描十万行的语句,则不需要回表。因此优化器评估这两条语句时,觉得回表查询更耗费时间,所以没有使用索引。但是实际中,这种方式并不是最优的。

3.解决办法

第一种解决办法是和第二条语句一样,采用force index强行选择一个索引。如果force index指定的索引在候选索引列表中,就直接选择这个索引,而不再去评估执行代价。但是这种方式不太优雅,而且改了索引名,语句也要改

第二种解决办法是考虑修改sql语句,引导MySQL使用我们期望的索引

第三种解决办法是新建一个更合适的索引,删除掉误用的索引

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

您可能感兴趣的文章:

相关文章

  • 还原大备份mysql文件失败的解决方法分享

    还原大备份mysql文件失败的解决方法分享

    今天在维护公司CRM的时候,恢复一个大的mysql数据库,恢复失败. 用下面方法解决(管理mysql用的是navicat).,设置以下几个参数的值后就正常了,以下语句也可以在mysql的控制台上执行
    2012-09-09
  • MYSQL 无法识别中文的永久解决方法

    MYSQL 无法识别中文的永久解决方法

    多数情况下,mysql在刚刚安装时是不支持中文的,这是由于编码的问题,那么如何解决这个方法,本文我们就来详细的了解一下
    2021-06-06
  • MySQL 5.7.18 release版安装指南(含有bin文件版本)

    MySQL 5.7.18 release版安装指南(含有bin文件版本)

    这篇文章主要介绍了MySQL 5.7.18 release版安装指南,本文只谈论release版,即含有bin文件版本,需要的朋友可以参考下
    2017-04-04
  • MySQL实现查询数据库表记录数

    MySQL实现查询数据库表记录数

    这篇文章主要介绍了MySQL实现查询数据库表记录数,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-09-09
  • 用percona-toolkit为MySQL收集系统和性能信息的教程

    用percona-toolkit为MySQL收集系统和性能信息的教程

    这篇文章主要介绍了用percona-toolkit为MySQL收集系统和性能信息的教程,可以轻松地将服务器基本信息录入数据库,需要的朋友可以参考下
    2015-11-11
  • MySQL中替代Like模糊查询的函数方式

    MySQL中替代Like模糊查询的函数方式

    这篇文章主要介绍了MySQL中替代Like模糊查询的函数方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • Mysql索引会失效的几种情况分析

    Mysql索引会失效的几种情况分析

    在做项目的过程中,难免会遇到明明给mysql建立了索引,可是查询还是很缓慢的情况出现,下面我们来具体分析下这种情况出现的原因及解决方法
    2014-06-06
  • MySQL多表连接查询详解

    MySQL多表连接查询详解

    这篇文章主要介绍了MySQL多表查询,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-08-08
  • MySQL判断查询条件是否包含某字符串的7种方式总结

    MySQL判断查询条件是否包含某字符串的7种方式总结

    SQLServer数据库死锁是指在多个事务同时访问数据库资源时,发生了互相等待对方所持有资源的情况,导致所有事务无法继续执行的现象,这篇文章主要给大家介绍了关于MySQL判断查询条件是否包含某字符串的7种方式,需要的朋友可以参考下
    2024-07-07
  • 解决MYSQL连接端口被占引入文件路径错误的问题

    解决MYSQL连接端口被占引入文件路径错误的问题

    下面小编就为大家带来一篇解决MYSQL连接端口被占引入文件路径错误的问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08

最新评论