草庐IT

#yyds干货盘点#MySQL学习-为啥有时候会选错索引

汤圆学Java 2023-03-28 原文
作者:汤圆

个人博客: javalover.cc

前言

大部分时候,系统选择的索引都是正确的,但是机器毕竟是机器,还是会有出错的时候;

今天就来分析一下,什么情况下会出现选错索引的问题,以及怎么去解决。

目录

  1. 选择索引的几个因素
  2. 索引的区分度
  3. 索引的采样统计
  4. 选错索引的解决办法

正文

1. 选择索引的几个因素

前面我们学习了一条SQL语句的查询和更新过程,知道了大概的流程为:连接器->分析器->优化器->执行器;

其中索引的选择就是在优化器那一步,因为选择一个好的索引就会对语句的优化起到关键的作用;

优化器选择索引,会考虑如下几个因素:

  1. 扫描的行数:

    这里的扫描行数不是真实的扫描行数,而是一个预估值;

    这个预估的扫描行数是根据索引的区分度来统计得出的;

    索引的区分度简单点来理解,就是一个表中的索引的不同值的个数,不同值越多,区分度越好(具体的统计细节下面会有介绍),

  2. 是否使用临时表:

    临时表顾名思义就是临时使用的表,在会话完成后就会结束;

    比如我们在使用union联合查询并集时,就会用到临时表,如下所示:可以看到 extra这一列显示为Using temporary就是用了临时表才存储查询结果;

  1. 是否需要排序:如果索引A需要排序,而索引B不需要排序,那么系统会优先考虑不需要排序的索引;

    你可能会有疑问,索引不都是有序的吗?怎么会需要排序呢

    这里排序的场景是:当筛选条件中有多个索引的情况下,且需要 order by 的场景,比如下面的语句:假设有10万条数据,a和b都是索引

    explain select * from t where (a between 1 and 10000) and (b between 10000 and 50000) order by b limit 1; 此时按扫描行数来看的话,应该是选择索引a,因为索引a只需要扫描1000行,而索引b需要扫描50000行;

    但是实际上系统选择的索引是b,通过执行explain可以看到,如下所示:

    这是因为如果用了索引a来查询数据,那么查询到的结果集还要根据b字段进行排序;

    在这里,系统自觉地认为排序会影响查询的性能,且影响要大于多扫描的5万行数据,所以就选择了索引b;

    这里我们可以试着把order by b 去掉,此时系统就会选择索引a;

  2. 还有其他的一些因素,比如是否需要回表等等;

2. 索引的区分度

索引的区分度:指的是索引上不同值的个数,也称为"基数"(cardinality);

当一个索引上,不同值的个数越多,基数就越大,这个索引的区分度越好;

通过如下的命令可以查看索引的区分度:

show index from t 打印如下所示:

系统是怎么取得这个基数值的呢?

首先可以肯定的是,基数值不是通过逐行扫描比对获取的,因为这样的话效率就太低了;

实际上系统的方法还是比较粗暴的,它是通过采样统计的方法来获取;

下面我们就介绍下采样统计;

3. 索引的采样统计

为啥要用采样统计呢?

就是上面我们介绍的,如果全表扫描的话效率很低,所以通过这种简单直接的方式,会提高效率,当然会损失一些精度;

那采样统计的流程是怎么样的呢?

首先系统会去取出N个数据页来做采样统计;这里的N是有默认值的,下面会介绍;

然后再统计每个数据页上不同值的个数,再做平均得到一个平均基数值 Avg;此时我们有了每个数据页上的基数值Avg;

最后再用Avg*数据页的数量,就是整个表的基数值。

采样统计的时机是什么时候?

在更新数据时,如果更新的记录数比例超过 1/M,就会重新执行一次采样统计(M有默认值,下面介绍);

怎么存储采样统计的结果?

通过设置 innodb_stats_persistent 的值来选对应的存储方式:

  1. on 统计信息会持久化存储,默认的N=20,M=10
  2. off 统计信息只会存在内存中,默认的N=8,M=16
如果采样统计偏差太大怎么办?

比如我们用explain命令查看的预估扫描行数为10000,但实际上通过show index 命令查看的基数值为20000多(可能由于多个会话同时更新数据导致);

这是我们就可以手动修正,命令如下:

analyze table t; 修正后,再次执行show index 就可以看到跟预估的扫描行数差不多了

4. 选错索引的解决办法

选错索引有多种情况,比如上面分析的预估扫描行数出错,需要排序等等,都会导致系统使用错误的索引;

当然办法总比困难多,下面就简单介绍下对应的解决办法;

  1. 预估扫描行数跟实际的差太多?
可以通过analyze table t来进行修正;

比如下面的例子,我本来只有2万多数据,用show index分析也是显示基数值为2万多;

但是当我把数据增加到5万多时,再次用show index分析还是显示2万多;

这时就可以执行analyze table t命令来修正,修正后如下所示,显示为5万多,正常了;

  1. 排序导致的索引选错:
解铃还须系铃人,既然排序导致的选错索引,那么我们可以修改排序;

比如下面的例子:

select * from t where (a between 1 and 10000) and (b between 10000 and 50000) order by b limit 1; 这里系统选择索引b的原因上面有介绍,就是系统认为索引b虽然扫描行数多,但是可以减少排序带来的性能消耗,所以系统选了索引b;

**那么我们可以将order by b改为order by b,a,**这样一来,索引b和索引a都需要排序,那么排序就不在性能考虑范围之内了,剩下的考虑因素就是扫描行数,此时系统就会选择索引a了;

不过这种改法会修改原有的语义,比如上面的例子只是返回一个数据limit 1;所以将order by b改为order by b,a都是返回结果集中b最小的那一个;

但是如果没有limit限制,那么改了之后返回的结果集顺序就不一致了;

还有一个办法就是删除索引b,前提是确保其他地方没有用到索引b;

  1. 通用的解决办法:
上面两种是针对特定的场景而言,其实有一个通用的办法就是强制系统选择某个索引,命令为:

force index (a) 不过这种办法缺点也很明显,就是不够敏捷,比如发现问题、修改索引、测试上线整个过程会比较耗时;

总结

上面分析了可能选错索引的几种情况:扫描行数、临时表、排序、回表等等

也介绍了对应的几种解决办法:分析表analyze table t、修改排序规则order by b,a、强制选择索引 force index( k )

不过系统选错索引的情况也不多见,基本上出现了就按照上面这几个思路去排查,问题应该就可以解决。

有时候感觉查询慢,就可以先通过explain可以查看系统使用的索引是不是预期的,然后再对症下药

有关#yyds干货盘点#MySQL学习-为啥有时候会选错索引的更多相关文章

  1. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  2. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  3. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  4. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

  5. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

    我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

  6. ruby-on-rails - 无法安装 mysql2 0.3.14 gem - 2

    我看到其他人也遇到过类似的问题,但没有一个解决方案对我有用。0.3.14gem与其他gem文件一起存在。我已经完全按照此处指示完成了所有操作:https://github.com/brianmario/mysql2.我仍然得到以下信息。我不知道为什么安装程序指示它找不到include目录,因为我已经检查过它存在。thread.h文件存在,但不在ruby​​目录中。相反,它在这里:C:\RailsInstaller\DevKit\lib\perl5\5.8\msys\CORE\我正在运行Windows7并尝试在Aptana3中构建我的Rails项目。我的Ruby是1.9.3。$gemin

  7. ruby - 如何使用 ruby​​ mysql2 执行事务 - 2

    我已经开始使用mysql2gem。我试图弄清楚一些基本的事情——其中之一是如何明确地执行事务(对于批处理操作,比如多个INSERT/UPDATE查询)。在旧的ruby-mysql中,这是我的方法:client=Mysql.real_connect(...)inserts=["INSERTINTO...","UPDATE..WHEREid=..",#etc]client.autocommit(false)inserts.eachdo|ins|beginclient.query(ins)rescue#handleerrorsorabortentirelyendendclient.commi

  8. ruby - 我如何学习 ruby​​ 的正则表达式? - 2

    如何学习ruby​​的正则表达式?(对于假人) 最佳答案 http://www.rubular.com/在Ruby中使用正则表达式时是一个很棒的工具,因为它可以立即将结果可视化。 关于ruby-我如何学习ruby​​的正则表达式?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1881231/

  9. 深度学习12. CNN经典网络 VGG16 - 2

    深度学习12.CNN经典网络VGG16一、简介1.VGG来源2.VGG分类3.不同模型的参数数量4.3x3卷积核的好处5.关于学习率调度6.批归一化二、VGG16层分析1.层划分2.参数展开过程图解3.参数传递示例4.VGG16各层参数数量三、代码分析1.VGG16模型定义2.训练3.测试一、简介1.VGG来源VGG(VisualGeometryGroup)是一个视觉几何组在2014年提出的深度卷积神经网络架构。VGG在2014年ImageNet图像分类竞赛亚军,定位竞赛冠军;VGG网络采用连续的小卷积核(3x3)和池化层构建深度神经网络,网络深度可以达到16层或19层,其中VGG16和VGG

  10. 机器学习——时间序列ARIMA模型(四):自相关函数ACF和偏自相关函数PACF用于判断ARIMA模型中p、q参数取值 - 2

    文章目录1、自相关函数ACF2、偏自相关函数PACF3、ARIMA(p,d,q)的阶数判断4、代码实现1、引入所需依赖2、数据读取与处理3、一阶差分与绘图4、ACF5、PACF1、自相关函数ACF自相关函数反映了同一序列在不同时序的取值之间的相关性。公式:ACF(k)=ρk=Cov(yt,yt−k)Var(yt)ACF(k)=\rho_{k}=\frac{Cov(y_{t},y_{t-k})}{Var(y_{t})}ACF(k)=ρk​=Var(yt​)Cov(yt​,yt−k​)​其中分子用于求协方差矩阵,分母用于计算样本方差。求出的ACF值为[-1,1]。但对于一个平稳的AR模型,求出其滞

随机推荐