查询慢的原因

在这里插入图片描述

优化数据访问

在这里插入图片描述

一个有趣的现象:如果可能的结果非常大的话,有可能就不用索引来排序了。
实际处理的时候,可能会有优化器对结果产生一些影响。
《高性能MySQL》中说整体数据访问量的阈值是30%,但是实际测试中并没有测出准确值,所以还是要根据实际案例,具体问题具体分析。
在这里插入图片描述

思考:下面这个limit怎么通过子查询进行优化?

在这里插入图片描述在这里插入图片描述

避免:

1、避免查询不需要的记录
我们常常会误以为mysql会只返回需要的数据,实际上mysql却是先返回全部结果再进行计算,在日常的开发习惯中,经常是先用select语句查询大量的结果,然后获取前面的N行后关闭结果集。
优化方式是在查询后面添加limit

2、避免多表关联时返回全部列

select * from actor inner join film_actor using(actor_id) inner join film using(film_id) where film.title='Academy Dinosaur';

select actor.* from actor...;

3、避免总是取出全部列
在公司的企业需求中,禁止使用select *,虽然这种方式能够简化开发,但是会影响查询的性能,所以尽量不要使用

4、避免重复查询相同的数据
如果需要不断的重复执行相同的查询,且每次返回完全相同的数据,因此,基于这样的应用场景,我们可以将这部分数据缓存起来,这样的话能够提高查询效率

执行过程的优化
查询缓存(8版本取消了)

在解析一个查询语句之前,如果查询缓存是打开的,那么mysql会优先检查这个查询是否命中查询缓存中的数据,如果查询恰好命中了查询缓存,那么会在返回结果之前会检查用户权限,如果权限没有问题,那么mysql会跳过所有的阶段,就直接从缓存中拿到结果并返回给客户端

查询优化处理

语法解析器和预处理
解析树:AST Tree
在这里插入图片描述
mysql通过关键字将SQL语句进行解析,并生成一颗解析树,mysql解析器将使用mysql语法规则验证和解析查询,例如验证使用使用了错误的关键字或者顺序是否正确等等,预处理器会进一步检查解析树是否合法,例如表名和列名是否存在,是否有歧义,还会验证权限等等

查询优化器

CBO:基于成本的优化
RBO:基于规则的优化

当语法树没有问题之后,相应的要由优化器将其转成执行计划,一条查询语句可以使用非常多的执行方式,最后都可以得到对应的结果,但是不同的执行方式带来的效率是不同的,优化器的最主要目的就是要选择最有效的执行计划

mysql使用的是基于成本的优化器,在优化的时候会尝试预测一个查询使用某种查询计划时候的成本,并选择其中成本最小的一个

last_query_cost查看上一次查询的代价
MySQL中可以通过show status like ‘last_query_cost’ 来查看查上一个查询的代价,而且它是io_cost和cpu_cost的开销总和,它通常也是我们评价一个查询的执行效率的一个常用指标。
(1)它是作为比较各个查询之间的开销的一个依据。
(2)它只能检测比较简单的查询开销,对于包含子查询和union的查询是测试不出来的。
(3)当我们执行查询的时候,MySQL会自动生成一个执行计划,也就是query plan,而且通常有很多种不同的实现方式,它会选择最低的那一个,而这个cost值就是开销最低的那一个。
(4)它对于比较我们的开销是非常有用的,特别是我们有好几种查询方式可选的时候。

在这里插入图片描述在这里插入图片描述在这里插入图片描述

关联查询

在这里插入图片描述

排序优化

无论如何排序都是一个成本很高的操作,所以从性能的角度出发,应该尽可能避免排序或者尽可能避免对大量数据进行排序。
推荐使用利用索引进行排序,但是当不能使用索引的时候,mysql就需要自己进行排序,如果数据量小则再内存中进行,如果数据量大就需要使用磁盘,mysql中称之为filesort。
如果需要排序的数据量小于排序缓冲区(show variables like ‘%sort_buffer_size%’😉,mysql使用内存进行快速排序操作,如果内存不够排序,那么mysql就会先将树分块,对每个独立的块使用快速排序进行排序,并将各个块的排序结果存放再磁盘上,然后将各个排好序的块进行合并,最后返回排序结果

两次传输排序
第一次数据读取是将需要排序的字段读取出来,然后进行排序,第二次是将排好序的结果按照需要去读取数据行。
这种方式效率比较低,原因是第二次读取数据的时候因为已经排好序,需要去读取所有记录而此时更多的是随机IO,读取数据成本会比较高
两次传输的优势,在排序的时候存储尽可能少的数据,让排序缓冲区可以尽可能多的容纳行数来进行排序操作

单次传输排序
先读取查询所需要的所有列,然后再根据给定列进行排序,最后直接返回排序结果,此方式只需要一次顺序IO读取所有的数据,而无须任何的随机IO,问题在于查询的列特别多的时候,会占用大量的存储空间,无法存储大量的数据
在这里插入图片描述

Logo

更多推荐