疫情跳广场舞?别忘啦继续学MySQL!!

大家好,我是亚索!

最近大连新一轮疫情又席卷而来,某高校封校后青年男女们竟在宿舍跳起了广场舞?!

疫情之下,大家仍不忘进行一些丰富的娱乐文化生活,亚索提醒大家在开心happy的同时,可不要忘了自身的工作和科研任务哦!


正题

今天继续来介绍有关MySQL的一些干货吧!主要内容有MySQL一条Sql语句执行很慢的原因?查询语句索引失效的原因?

这是后端Java面试官最爱问知识之一,据说也是 腾讯面试问过的真题 哈!知识介绍均来自互联网搜集资料以及课本笔记,如有重复涉及版权问题,纯属偶然,还请私信纠正一下哈。

Sql执行很慢原因:分类讨论!

亚索觉得该问题属于开放性范畴,就像百度曾经问过的面试题:浏览器输入url回车之后,会发生什么? 细说起来会涉及到计算机网络的很多知识,sql语句执行很慢也能涉及到MySQL很多核心知识。

遇到问题不要慌张,首先理清楚逻辑,理性思考,亚索觉得这个问题则需要分类讨论:

  • 在客观条件(数据量等)不变的情况下,该Sql语句一直执行的很慢。

  • 该Sql语句在大多数情况下执行速度正常,在少数情况出现执行过慢的问题。

Sql执行一直很慢的情况

在MySQL数据量不变的情况下,Sql语句执行一直很慢,那么可能就是Sql语句书写的问题或者索引命中存在问题了。

比如有一个表,建表语句如下:

mysql> CREATE TABLE `t_da_age` (
  `id` int(11) NOT NULL,
  `no` int(11) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

Sql查询语句如下:

select * from t_da_age where 30 <age 
and age < 50;

那么这条语句查询效率慢,原因可能是:

1、age字段上并未建立索引,此时数据库则会自动走全表扫描,效率很低,很慢。

2、如果age字段上加了索引,但是由于一些错误的Sql书写方式,又会导致索引失效,数据库还是自动走全表扫描,比如语句为:

select * from t_da_age where age - 1 = 100;
select * from t_da_age where abs(age) = 30;
//假设abs是求绝对值的函数,以上无法命中索引

这种对索引字段在符号左边进行计算,或者进行函数操作的方式,是无法命中索引,亦称为索引失效。但若是"where age = 1+100",这种是可以命中索引的。

另外,可以通过在sql语句前加上explain命令这种方式查看sql语句是否用到索引。

3、更深层次地,还有一个原因就是数据库自己不选索引,走全表扫描!或者选错了别的索引!

这个也可以归结为数据库索引失效情况之一,但这是数据库自己本身执行语句时出现了问题,不是Sql语句书写的毛病,比如:

select * from t_da_age where 30 < age and 
age < 50;

这种情况下,就算age字段上建立了索引,数据库系统也不一定会走该索引,不一定是先查到age对应B+树的叶子节点中主键的值,再通过主键索引查询需要的整行数据返回(主键索引与非主键索引的区别,以后会介绍)。

系统是有可能直接扫描全表,找出符合"30 < age and age < 50"的整行数据。

数据库计算索引基数,存在采样失误

系统在执行这条语句的时候,会进行预测,即在走age字段索引,需要扫描多少行数据,如果预测到扫描行数过多,系统就可能不走索引而直接走全表扫描了。

数据库系统是通过遍历索引字段的部分数据,即通过 采样统计 的方式,预测计算出索引的基数。基数也叫做区分度,一个索引字段在一定范围内不同的值越多,意味着出现相同数值的索引越少,索引区分度就越高,即意味着age在30-50这个范围内的数据行数就越少。

假如age字段的基数(区分度)实际上是很大的,但索引系统在采样统计的时候,恰巧遍历了基数很小的那一部分数据(即重复率较高),然后就误以为该索引基数很小,在查询时数据库觉得走全表扫描更加高效,同样会导致最终不选索引,索引失效!!

注意:查询语句是否需要排序、是否需要使用临时表也是会影响系统选择的

有时候可以通过强制走索引的方式查询数据,比如:

select * from t force index(a) where age
< 50 and age > 30;

或者可以用这条语句重新统计索引的基数:

analyze table t_da_age;

也可以查看索引字段的基数等相关信息:

show index frow t_da_age;

既然数据库会算错索引的基数,如果查询语句有涉及多个索引的时候,系统也有可能会选错走别的索引,这也同样可能会导致Sql执行很慢!

补充总结一下索引失效的原因有哪些:

  • where查询索引字段在符号左边有计算或者对索引字段进行函数计算

  • 数据库对每个索引通过采样计算区分度,可能产生误采样导致认为该索引区分度小,走该索引查询不如走全表扫描更快,最终选择不走索引

  • like模糊查询中有以%开头

  • 联合索引使用时未遵守最左前缀原则,where未用左列字段或者左边连续几列字段,比如a、b、c是联合索引,查询条件为where b=3,也会索引失效

  • where no = 3 or id = 4查询条件有or,必须所有列全建立索引,否则也会索引失效

Sql执行偶尔很慢的情况

一条Sql语句执行偶尔很慢,亚索觉得这个语句可能是没有什么问题的,那原因主要有两个。

1、语句执行时,数据库正在刷脏页

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

当内存数据页跟磁盘数据页内容不一致的时候,该内存页称为"脏页"。内存数据写入到磁盘后,内存和磁盘上的数据页的内容就一致了,称为"干净页"。

数据库通过 redo log 将数据更新到磁盘中去的操作可以称为"刷脏页"。刷脏页有很多种场景,常见有4种,性能方面主要关注前2种:

  • 内存不够用:如果一次查询较多的数据,恰好碰到所查数据页不在内存中时,需要申请内存,而此时恰好内存不足,就需要淘汰一部分内存数据页,如果是干净页,就直接释放,如果恰好是脏页就需要刷脏页

  • redo log不够用:redo log 里的容量是有限的,如果数据库更新频繁,redo log 很快就会被写满了,这个时候不能等到空闲的时候再把数据同步到磁盘的,只能暂停其他操作,全身心来把数据同步到磁盘中去的,这就会导致我们平时执行正常的SQL语句突然执行很慢

  • MySQL 认为系统“空闲”的时候

  • MySQL 正常关闭的时候:MySQL会把内存的脏页都 flush 到磁盘上,这样下次 MySQL 启动的时候,就可以直接从磁盘上读数据。

2、Sql执行时遇到锁,如表级锁、行级锁

刚好这条语句涉及到的表,别的语句或者线程在用,并且加锁了,拿不到锁,只能慢慢等待别人释放锁了(表级锁)。或者,表没有加锁,但要使用到的某一行被加锁了(行级锁)。

如果要判断是否真的在等待锁,我们可以用show processlist这个命令来查看当前的状态。

小结

本文主要介绍了一下MySQL中一条Sql语句执行很慢的原因,大家如果在面试中被问到,一定要记得分类讨论哦!

总结一下,一条Sql语句执行很慢,分类讨论:

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

  • 没有用上索引:例如该字段没有索引;由于对字段进行运算、函数操作等导致索引失效。

  • 数据库基数的采样计算有误,选错了索引或者不选索引。

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

  • 数据库正在刷脏页,例如 内存不够用了或者 redo log 写满了需要同步到磁盘。

  • 执行Sql的时候,遇到锁,如表级锁或者行级锁。

大家面试的时候,说完提纲要点以后,亚索觉得最好能详细展开陈述哈!说的越深,面试加分越多!


好了今天就说到这,我是亚索,关注下面vx公众号,大家一起学Java吧!

全部评论

相关推荐

用户64975461947315:这不很正常吗,2个月开实习证明,这个薪资也还算合理,深圳Java好多150不包吃不包住呢,而且也提前和你说了没有转正机会,现在贼多牛马公司骗你说毕业转正,你辛辛苦苦干了半年拿到毕业证,后面和你说没hc了😂
点赞 评论 收藏
分享
面试官人很好,态度和蔼可亲,没答出来时也会引导你去思考。由于是晚上面的,导致我白天一天都有点紧张,面的时候状态也不是很好,正常可能面试官提问完应该思考几秒再答,而我就像抢答一样一口气把所有会的都说出来,这样就导致逻辑比较混乱,东一句西一句的。首先是自我介绍,先把会的技术大致讲一下,由于我八股背的多所以着重讲了一下,Java,go,jvm,MySQL,Redis,计网,操作系统这些,然后一小部分闲聊,然后先问了一下项目,面试官问我这个项目是否落实之类的,直接坦言说是写的练手的,包括之前也写过IM通讯,外卖之类的。然后面试官就把提问的重点放在了八股上。先问了Java:类加载器(答:3种+自定义类加载器、tomcat、原因+双亲委派+好处)JVM参数(答:xmx,xms,newsize这些,问我是如何设定的,我回答是把内存分一半给堆,再把堆分一半给新生代,这方面确实不太了解)然后问了一下并发相关的:线程池(答:线程池的7个参数(忘了线程工厂和阻塞时间了),3个重要参数,还有线程如何启用,为什么要设计最大线程数之类的,提到Java栈默认分配1MB运行时不可以更改)AQS(答:先讲clh是自旋锁+list,然后是AQS在这个基础上做的两个优化,然后举了一下reentrantlock根据state如何获取资源)CAS(答:使用三个字段,aba问题,然后将通常搭配自旋锁实现,面试官问通常会自旋多少次,这个不太了解,答的100,然后问100次大概多少秒,回答微秒级,然后面试官讲了一下怎么做资源可能没用完,意识到可能还需要进行阻塞操作)然后考虑一下Linux命令(top,ps,如何使用管道符过滤线程和使用Linux启动线程没答出来)然后问Redis:持久化机制(答:三种aof,rdb,混合,aof的三个参数刷盘策略,rdb以快照保存,使用bgsave会使用子线程来保存不会阻塞,而aof虽然会阻塞但是只在写完数据后追加一条命令,不会太影响,然后是他俩的优缺点,还有混合是怎么保存数据的)集群模式(答:三种,主从复制到缺点再到哨兵机制,正常使用三个哨兵互相监督,主节点挂了投票选主哨兵然后选主节点,然后额外讲一下脑裂的问题,主节点进行数据更新然后把命令写入aof来同步从节点,最后cluster集群,如何实现,使用16383个哈希槽(艹答成16384了),先根据哈希码取余,再根据节点数取余决定放在哪个节点上,然后问了一下我会怎么选集群模式,首先是cluster的问题,会让管道操作之类的失效,然后哨兵会导致整个集群结构变得复杂,使用小项目可能会考虑哨兵,大的考虑cluster,然后考了一下cluster如果一个节点挂了怎么办,根据节点数重新取余然后数据转移,面试官说这么转移比较慢,有没有别的办法,我隐约记得使用一个类似环形数组的方式,想不起来了)然后考了一下MySQL的b+树(这方面的知识点太多了,导致我什么都想讲逻辑就比较乱,讲了一下聚簇索引,树的叶子节点对应着一张页16KB,MySQL有一个区的概念,把这些页放在同一个区中,这样叶子节点的双向链表遍历时速度更快,然后b+树的扇出比较大(非常二,说成扇度之类的,面试官以为说的是扇区)这样层数就比较小,一行1kb数据的话3层可以放心2000w数据)其他的暂时想不起来了算法是lru,面试官问要不要提示,我说写个,然后写了10分钟左右,说大概写好了,但是面试官指出了2个小错误,第一个马上就改回来了,第二个一直没看出来(大脑这时候已经停止工作了)反问:问学习建议,说根据实际的项目进行深入,考虑应该怎么做,还问了一下组里面是做Java的吗?面试官说他是做go的,组里什么语言都有,语言影响不大,连忙补充了一句我对go的底层有深入源码的学习)结束。总体感觉答得不太好,没有太体现出深度,细节也不够全面。
下一个更好呗:佬,我投完云智一直没消息,多久约的一面啊
查看14道真题和解析
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务