腾讯面试:一条SQL语句执行得很慢的原因有哪些?


戳蓝字“CSDN云计算”关注我们哦!

640?wx_fmt=jpeg

技术头条:干货、简洁、多维全面。更多云计算精华知识尽在眼前,get要点、solve难题,统统不在话下!

  

作者:帅地

转自:苦逼的码农


说实话,这个问题可以涉及到 MySQL 的很多核心知识,可以扯出一大堆,就像要考你计算机网络的知识时,问你“输入URL回车之后,究竟发生了什么”一样,看看你能说出多少了。

之前腾讯面试的实话,也问到这个问题了,不过答的很不好,之前没去想过相关原因,导致一时之间扯不出来。所以今天,我带大家来详细扯一下有哪些原因,相信你看完之后一定会有所收获,不然你打我。

一、开始装逼:分类讨论

一条 SQL 语句执行的很慢,那是每次执行都很慢呢?还是大多数情况下是正常的,偶尔出现很慢呢?所以我觉得,我们还得分以下两种情况来讨论。

1、大多数情况是正常的,只是偶尔会出现很慢的情况。

2、在数据量不变的情况下,这条SQL语句一直以来都执行的很慢。

针对这两种情况,我们来分析下可能是哪些原因导致的。

二、针对偶尔很慢的情况

一条 SQL 大多数情况正常,偶尔才能出现很慢的情况,针对这种情况,我觉得这条SQL语句的书写本身是没什么问题的,而是其他原因导致的,那会是什么原因呢?

1、数据库在刷新脏页我也无奈啊

当我们要往数据库插入一条数据、或者要更新一条数据的时候,我们知道数据库会在内存中把对应字段的数据更新了,但是更新之后,这些更新的字段并不会马上同步持久化到磁盘中去,而是把这些更新的记录写入到 redo log 日记中去,等到空闲的时候,在通过 redo log 里的日记把最新的数据同步到磁盘中去。

不过,redo log 里的容量是有限的,如果数据库一直很忙,更新又很频繁,这个时候 redo log 很快就会被写满了,这个时候就没办法等到空闲的时候再把数据同步到磁盘的,只能暂停其他操作,全身心来把数据同步到磁盘中去的,而这个时候,就会导致我们平时正常的SQL语句突然执行的很慢,所以说,数据库在在同步数据到磁盘的时候,就有可能导致我们的SQL语句执行的很慢了。

2、拿不到锁我能怎么办

这个就比较容易想到了,我们要执行的这条语句,刚好这条语句涉及到的,别人在用,并且加锁了,我们拿不到锁,只能慢慢等待别人释放锁了。或者,表没有加锁,但要使用到的某个一行被加锁了,这个时候,我也没办法啊。

如果要判断是否真的在等待锁,我们可以用 show processlist这个命令来查看当前的状态哦,这里我要提醒一下,有些命令最好记录一下,反正,我被问了好几个命令,都不知道怎么写,呵呵。

下来我们来访分析下第二种情况,我觉得第二种情况的分析才是最重要的

三、针对一直都这么慢的情况

如果在数据量一样大的情况下,这条 SQL 语句每次都执行的这么慢,那就就要好好考虑下你的 SQL 书写了,下面我们来分析下哪些原因会导致我们的 SQL 语句执行的很不理想。

我们先来假设我们有一个表,表里有下面两个字段,分别是主键 id,和两个普通字段 c 和 d。

mysql> CREATE TABLE `t` (
  `id` int(11NOT NULL,
  `c` int(11DEFAULT NULL,
  `d` int(11DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

1、扎心了,没用到索引

没有用上索引,我觉得这个原因是很多人都能想到的,例如你要查询这条语句

select * from t where 100 <c and c < 100000;

(1)、字段没有索引

刚好你的 c 字段上没有索引,那么抱歉,只能走全表扫描了,你就体验不会索引带来的乐趣了,所以,这回导致这条查询语句很慢。

(2)、字段有索引,但却没有用索引

好吧,这个时候你给 c 这个字段加上了索引,然后又查询了一条语句

select * from t where c - 1 = 1000;

我想问大家一个问题,这样子在查询的时候会用索引查询吗?

答是不会,如果我们在字段的左边做了运算,那么很抱歉,在查询的时候,就不会用上索引了,所以呢,大家要注意这种字段上有索引,但由于自己的疏忽,导致系统没有使用索引的情况了。

正确的查询应该如下

select * from t where c = 1000 + 1;

有人可能会说,右边有运算就能用上索引?难道数据库就不会自动帮我们优化一下,自动把 c - 1=1000 自动转换为 c = 1000+1。

不好意思,确实不会帮你,所以,你要注意了。

(3)、函数操作导致没有用上索引

如果我们在查询的时候,对字段进行了函数操作,也是会导致没有用上索引的,例如

select * from t where pow(c,2) = 1000;

这里我只是做一个例子,假设函数 pow 是求 c 的 n 次方,实际上可能并没有 pow(c,2)这个函数。其实这个和上面在左边做运算也是很类似的。

所以呢,一条语句执行都很慢的时候,可能是该语句没有用上索引了,不过具体是啥原因导致没有用上索引的呢,你就要会分析了,我上面列举的三个原因,应该是出现的比较多的吧。


2、呵呵,数据库自己选错索引了

我们在进行查询操作的时候,例如

select * from t where 100 < c and c < 100000;

我们知道,主键索引和非主键索引是有区别的,主键索引存放的值是整行字段的数据,而非主键索引上存放的值不是整行字段的数据,而且存放主键字段的值。不大懂的可以看我这篇文章:面试小知识:MySQL索引相关    里面有说到主键索引和非主键索引的区别

也就是说,我们如果走 c 这个字段的索引的话,最后会查询到对应主键的值,然后,再根据主键的值走主键索引,查询到整行数据返回。

好吧扯了这么多,其实我就是想告诉你,就算你在 c 字段上有索引,系统也并不一定会走 c 这个字段上的索引,而是有可能会直接扫描扫描全表,找出所有符合 100 < c and c < 100000 的数据。

为什么会这样呢?

其实是这样的,系统在执行这条语句的时候,会进行预测:究竟是走 c 索引扫描的行数少,还是直接扫描全表扫描的行数少呢?显然,扫描行数越少当然越好了,因为扫描行数越少,意味着I/O操作的次数越少。

如果是扫描全表的话,那么扫描的次数就是这个表的总行数了,假设为 n;而如果走索引 c 的话,我们通过索引 c 找到主键之后,还得再通过主键索引来找我们整行的数据,也就是说,需要走两次索引。而且,我们也不知道符合 100 c < and c < 10000 这个条件的数据有多少行,万一这个表是全部数据都符合呢?这个时候意味着,走 c 索引不仅扫描的行数是 n,同时还得每行数据走两次索引。

所以呢,系统是有可能走全表扫描而不走索引的。那系统是怎么判断呢?

判断来源于系统的预测,也就是说,如果要走 c 字段索引的话,系统会预测走 c 字段索引大概需要扫描多少行。如果预测到要扫描的行数很多,它可能就不走索引而直接扫描全表了。

那么问题来了,系统是怎么预测判断的呢?这里我给你讲下系统是怎么判断的吧,虽然这个时候我已经写到脖子有点酸了。

系统是通过索引的区分度来判断的,一个索引上不同的值越多,意味着出现相同数值的索引越少,意味着索引的区分度越高。我们也把区分度称之为基数,即区分度越高,基数越大。所以呢,基数越大,意味着符合 100 < c and c < 10000 这个条件的行数越少。

所以呢,一个索引的基数越大,意味着走索引查询越有优势。

那么问题来了,怎么知道这个索引的基数呢?

系统当然是不会遍历全部来获得一个索引的基数的,代价太大了,索引系统是通过遍历部分数据,也就是通过采样的方式,来预测索引的基数的。

扯了这么多,重点的来了,居然是采样,那就有可能出现失误的情况,也就是说,c 这个索引的基数实际上是很大的,但是采样的时候,却很不幸,把这个索引的基数预测成很小。例如你采样的那一部分数据刚好基数很小,然后就误以为索引的基数很小。然后就呵呵,系统就不走 c 索引了,直接走全部扫描了

所以呢,说了这么多,得出结论:由于统计的失误,导致系统没有走索引,而是走了全表扫描,而这,也是导致我们 SQL 语句执行的很慢的原因。

这里我声明一下,系统判断是否走索引,扫描行数的预测其实只是原因之一,这条查询语句是否需要使用使用临时表、是否需要排序等也是会影响系统的选择的。

不过呢,我们有时候也可以通过强制走索引的方式来查询,例如

select * from t force index(a) where c < 100 and c < 100000;

我们也可以通过

show index from t;

来查询索引的基数和实际是否符合,如果和实际很不符合的话,我们可以重新来统计索引的基数,可以用这条命令

analyze table t;

来重新统计分析。

既然会预测错索引的基数,这也意味着,当我们的查询语句有多个索引的时候,系统有可能也会选错索引哦,这也可能是 SQL 执行的很慢的一个原因。

好吧,就先扯这么多了,你到时候能扯出这么多,我觉得已经很棒了,下面做一个总结。

四、总结

以上是我的总结与理解,最后一个部分,我怕很多人不大懂数据库居然会选错索引,所以我详细解释了一下,下面我对以上做一个总结。

一个 SQL 执行的很慢,我们要分两种情况讨论:

1、大多数情况下很正常,偶尔很慢,则有如下原因

(1)、数据库在刷新脏页,例如 redo log 写满了需要同步到磁盘。

(2)、执行的时候,遇到锁,如表锁、行锁。

2、这条 SQL 语句一直执行的很慢,则有如下原因。

(1)、没有用上索引:例如该字段没有索引;由于对字段进行运算、函数操作导致无法用索引。

(2)、数据库选错了索引。

大家如果有补充的,也是可以留言区补充一波哦。

640?wx_fmt=png


福利

扫描添加小编微信,备注“姓名+公司职位”,加入【云计算学习交流群】,和志同道合的朋友们共同打卡学习!


640?wx_fmt=jpeg


推荐阅读:

  • 10 种最流行的 Web 挖掘工具 | 程序员硬核评测

  • 《复联4》| 生活需要漫威这块糖

  • 如何向 6 岁的孩子解释编程?这个解释厉害了

  • “踏实工作 7 年,辞职时老板头都不抬”

  • 60倍回报! AI工程师用OpenAI创建了一个比特币自动交易工具! 这里是详细做法 | 技术头条

  • 赌5毛钱,你解不出这道Google面试题


640?wx_fmt=png真香,朕在看了!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/523761.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QT中将float数转换为QString类型

概述 直接上代码&#xff0c;下面是示例&#xff1a; 示例一 float up 23.45; QString str QString::number(up ,f,2);示例二 float up 23.45; QString str QString::asprintf("%.2f",up);示例三 float up 23.45; QString str; str str.setNum(up,f,2);示…

分布式精华问答 | 分布式系统面临哪些挑战?​

布式的处理方式越来越受到业界的青睐——计算机系统正在经历一场前所未有的从集中式向分布式架构的变革。今天&#xff0c;我们就来看看关于分布式的精华问答吧&#xff01;1Q&#xff1a;什么是分布式缓存&#xff1f;A&#xff1a;为了提高性能和响应时间&#xff0c;在应用程…

Spring4.x集成xfire1.26 问题汇总

WebService专栏之xfire集成异常总览 文章目录一、asm.jar包冲突1.1 问题现象1.2 问题定位&#xff1a;1.3 解决方案&#xff1a;二、 Old 1.x singleton attribute in use2.1 问题现象2.2 问题定位2.3 解决方案&#xff1a;三、Unrecognized xbean element mapping3.1 问题定位…

qt中十进制转换为十六进制和二进制字符串,以及二进制字符串转十进制,十六进制字符串

概述 直接上代码&#xff0c;记录一下&#xff0c;方便日后使用&#xff1a; 示例一 将十进制转换为二进制和十六进制字符串&#xff1b; int num 23; QString str QString::number(num,16);//转换为十六进制 str QString::number(num,2);//转换为二进制示例二 将二进制…

如何使用「番茄法」高效的写算法题?

戳蓝字“CSDN云计算”关注我们哦&#xff01; 作者&#xff1a;侯振宇转自&#xff1a;五分钟学算法01 目的 持续做算法题的目的仍然是自身能力提升。可以继续细化成三点:保持思维敏捷。非常重要&#xff0c;状态好才能保持对编程的热情。对基础的数据结构、查找和排序保持熟练…

双因子认证(Two-factor authentication)

一、简介 简言之&#xff0c;双因素身份验证&#xff08;也称为“两步验证”&#xff09;是指身份验证涉及两个阶段——通常是除了常规密码&#xff09;之外的某种一次性密码&#xff08;OTP&#xff1a;One-Time Password&#xff09;。网上银行已经使用这种方法很长一段时间了…

Spring中,使用工具类无法自动注入service

这个问题我其实遇到很久了&#xff0c;目前解决方案是手动注入service&#xff0c;并且把工具类也手动注入。 场景&#xff1a; 在spring中&#xff0c;我们经常会定义工具类来做一些奇怪的事情&#xff0c;我当前是通过定时任务quarz调度工具类&#xff0c;工具类再调servic…

sizeof与strlen使用中的问题

概述 直接上代码&#xff0c;使用中经常会涉及到sizeof与strlen计算的问题&#xff0c;下面看例子&#xff1a; char *pstr "hello"; char pstr[] "hellonini"; char pstr[6] "hello";上述求sizeof(pstr)的值&#xff0c;分别为&#xff1a…

从人工智能到云,英特尔开源技术推动软件栈创新

戳蓝字“CSDN云计算”关注我们哦&#xff01;2019年英特尔开源技术峰会&#xff08;OSTS&#xff09; 【CSDN记者现场报道】5月14-16日&#xff0c;英特尔主办一年一度的开源技术峰会&#xff08;OSTS&#xff09;。该峰会源自2004年的一次内部会议&#xff0c;从最初只有几十个…

Axis2搭建WebService服务

使用Axis2搭建WebService服务 文章目录一、服务端部署1.1 在web.xml配置文件中添加映射路径&#xff1a;2. 创建目录及文件3. 新建服务接口4. 新建接口实现类5. 发布服务6. 浏览器测试二、客户端部署2.1 Axis2客户端通用工具类封装(企业版本)2.2 单元测试(命名空间默认)&#x…

c++实现引用计数

概述 当有指针指向同一块内存空间时&#xff0c;计数器加1&#xff0c;没增加一个指向该内存空间的指针&#xff0c;计数器加1&#xff0c;同理&#xff0c;当原本指向该内存空间的指针指向另一块内存&#xff0c;计数器减1&#xff0c;被指向的另一个内存的计数器加1。下面是…

焦虑的 BAT、不安的编程语言,揭秘程序员技术圈生存现状!

戳蓝字“CSDN云计算”关注我们哦&#xff01;【CSDN 编者按】在迭代不休的技术圈中&#xff0c;仅在过去的一个月期间&#xff0c;我们见证了有史以来第一张黑洞照片的诞生&#xff1b;经历了为让人义愤填膺的 996&#xff1b;思考了作为程序员的年龄之槛&#xff1b;膜拜了技术…

axis2手动设置命名空间targetNamespace

修改services.xml&#xff1a; name &#xff1a;你暴露的接口服务名 targetNamespace 命名空间 <service name"ws" targetNamespace"url" >,添加targetNamespace属性&#xff0c;然后添加标签&#xff1a; <schema schemaNamespace"url&…

5G精华问答 | 除了速度,5G还能带来什么?

从2016年以来&#xff0c;5G热度逐步攀升。作为下一代移动通信网络&#xff0c;如果用一个关键词来形容5G&#xff0c;那就是“快”。5G不仅会极大地改变人们现有的生活和工作方式&#xff0c;提升通信效率&#xff0c;还可以加大很多前沿技术和产品落地的可能性。今天&#xf…

Spring获取JavaBean的xml形式和注解形式

Spring获取JavaBean的xml形式和注解形式 文章目录一、用xml文件方式管理JavaBean1. 创建一个xml配置文件2. 将一个Bean交由spring创建并管理3. 获取Spring上下文&#xff0c;获取bean二、用注解获取Javabean1. 创建一个class配置java文件2. 将一个bean交由Spring创建并管理3. 获…

C++中两个栈实现一个队列

引言 首先看这个标题的时候&#xff0c;需要联想到栈和队列的特点&#xff0c;栈是先进后出&#xff0c;队列是先进先出。假如三个元素1&#xff0c;2&#xff0c;3&#xff0c;将这三个元素依次入栈1后&#xff0c;再将栈1中元素依次出栈放入到栈2中&#xff0c;栈1中只留下最…

Kube-OVN:基于OVN的开源Kubernetes网络实践

戳蓝字“CSDN云计算”关注我们哦&#xff01;技术头条&#xff1a;干货、简洁、多维全面。更多云计算精华知识尽在眼前&#xff0c;get要点、solve难题&#xff0c;统统不在话下&#xff01;今天&#xff0c;许多企业开始运行Kubernetes集群&#xff0c;并从中受益。但我们仍然…

IntelliJ IDEA中创建xml文件

1、file—setting&#xff0c;左上角输入template&#xff0c;2、在左侧栏找到File And Code Templates3、中间选中Files4、点击号&#xff0c;添加模板5、输入模板名字&#xff1a;Name:mybatis-cfg.xml &#xff08;name可以自定义&#xff09;6、后缀名extension&#xff1a…

C++冒泡排序

引言 冒泡排序作为排序中一个比较重要的方法&#xff0c;这里做一些简单的记录。 示例 本例中将一组数据2&#xff0c;4&#xff0c;3&#xff0c;8&#xff0c;5按照从小到大的顺序进行冒泡排序。首先说一下&#xff0c;冒泡排序是怎么排序的&#xff1a;将数组中相邻的两个…

Spring概念理解

什么是IOC&#xff1f; 控制反转&#xff0c;依赖注入 1、控制什么&#xff1f; 控制对象的创建及销毁(生命周期) 2、反转什么&#xff1f; 讲对象的控制权交给IOC容器