mysql调优三-查询优化

mysql调优-查询优化



一、影响查询速度因素

  网络,CPU,IO,上下文切换,系统调用,生成统计信息(监控类),锁等待时间。

二、优化表数据

1.数据量

    查询慢的主要原因是访问的数据太多,某些查询不可避免的需要筛选大量的数据,可以通过减少访问数据量的方式进行优化。需要确认查询是否在检索大量超过需要的数据,确认mysql服务器层是否在分析大量超过需要的数据行。
    检查sql是否查询了不需要的数据,如果可以用limit就尽量用,需要多少拿多少;多表关联时只取自己需要的列,不使用select * 会影响查询性能;如果需要多次查询重复的数据,可以使用缓存将该部分数据缓存起来。

三、执行过程优化

1.查询缓存

    查询缓存是打开的,那么mysql会优先检查这个查询是否命中查询缓存中的数据,查询命中了查询缓存,在返回结果之前会检查用户权限,如果权限没有问题,那么mysql会跳过所有的阶段,就直接从缓存中拿到结果并返回给客户端。
    查看缓存相关信息:show variables like ‘%query_cache%’;
    query_cache_type为ON是打开状态OFF是关闭状态;query_cache_size缓存的最大大小。
    如果缓存开关是OFF需要去配置文件修改相关配置:
my.cnf文件的mysqld中添加

query_cache_type = ON
query_cache_size = 10M

2.语法解析器预处理

    mysql通过sql中的关键字进行解析生成解析树,再通过mysql语法规则验证和解析查询进行预处理,检查解析树是否合理。

3.查询优化器

    1>、优化器优化策略
        静态优化:直接对解析树进行分析,并完成优化;
        动态优化:与查询的上下文有关,也可能跟取值、索引对应的行数有关;
        mysql对查询的静态优化只需要一次,对动态优化在每次执行时需要重新评估;

    2>、优化器优化类型
        定义关联表顺序:在多表关联时并不是按sql所写关联表的顺序来执行的,而是由优化器决定顺序;
        将外连接转化为内连接,效率更高;
        使用等价变换规则:使用一些等价变化来简化并规划表达式;
        优化count(),min(),max():如查找某列最小值,直接查询对应索引最左端的值,避免全表扫描;
        转化常数表达式:检测到表达式可以转化为常数时,会把该表达式作为常数进行处理;
        覆盖索引:当索引中的列包含所有查询中需要使用的列的时候,使用覆盖索引;
        子查询优化:子查询会生成临时表,在某些情况下将子查询转换一种效率更高的形式,将子查询数据放入缓存,减少多个查询多次对数据进行访问;
        等值传播:两个列的值通过等式关联,mysql会把其中一个列的where条件传递到另一个上;

4.关联查询

    1>、关联查询过程:
    mysql对所有关联都执行嵌套循环关联操作,也就是mysql先在一张表中循环取出单条数据,然后再嵌套到下一个表中寻找匹配的行,依次下去,直到找到所有表中匹配的行为止。然后根据各个表匹配的行,返回查询中需要的各个列。
    2>、join方式实现原理(三种):
        Simple Nested-Loop Join:连接的两个表将其中一个作为驱动表,另外一个作为匹配表,外部循环逐行消耗驱动表,从驱动表中分别取出每一条记录去匹配匹配表,最后合并数据,对数据库消耗比较大。下图中左侧为驱动表数据,右侧为匹配表数据。
Nested Loops Join
        Index Nested-Loop Join:匹配表上有索引,且可以通过索引来减少比较次数来加快查询速度。查询时,驱动表会根据关联字段的索引进行查找,匹配到索引后进行回表查询。匹配表的关联键是主键时,性能最高。
Index Nested-Loop Join
        Block Nested-Loop Join:第一种方式的改进版,如果join列没有索引,采用该方式。中间有join buffer,先将驱动表所有join相关列都先缓存到join buffer中,批量与匹配表进行匹配,降低匹配表的访问频率。(默认join_buffer_size为256K)
Block Nested-Loop Join
      拓展:Join Buffer:Join Buffer会缓存所有参与查询的列而不是只有Join的列;join_buffer_size的最大值在5.1.22前为4G之后版本可以大于4G;使用Block Nested-Loop Join算法需要开启优化器管理配置的optimizer_switch,设置block_nested_loop为on,默认为开启,查看当前的信息show variables like ‘%optimizer_switch%’;

三、特定类型查询优化

1.优化count()查询

        可以使用近似值,如果不需要完全精确的值,可以使用explain来获取近似的值,或者说使用hyperloglog算法来获取(gitHub有大量插件)。更为实际的方法是使用索引覆盖扫描,或者增加汇总表,也可引入外部缓存系统将值进行缓存。

2.优化关联查询

        确保on或者using子句中的列上有索引,在创建索引的时候需要考虑到关联的顺序;
groupby和order by中的表达式只涉及到一个表中的列的时候mysql才有可能使用索引来优化这个过程。

3.优化limit分页

        最有效的是使用覆盖索引,而不是查询所有的列。
        当偏移量非常大时,前面的大部分数据被抛弃,造成浪费,对比下面两个执行计划:

explain select id,user_name from ur_user order by age limit 5000000,5;
explain select t1.id,t1.user_name from ur_user AS t1 inner join (select id from ur_user order by age limit 5000000,5) AS t2 using(id);

4.优化union查询

        mysql是通过创建填充临时表的方式来执行union查询,很多优化策略在union查询中没法很好的使用。需要手工的将where、limit、order by等子句下推到各个子查询中,方便优化器可以充分利用这些条件进行优化。
        如果可以用union all就尽量用,如果没有all关键字会在查询时给临时表加distinct关键字,这个操作的代价很高。

四、自定义变量

        推荐使用自定义变量,比较方便。

1.基本使用

基本赋值:set @rank :=1;
查询/表达式赋值:set @min_user_id :=(select min(user_id) from ur_user);

2.限制

        无法使用查询缓存;
        无法在使用常量或者标识符的地方使用自定义变量,如表名、列名、limit子句;
        不能显式地声明自定义变量地类型;
        赋值符号:=的优先级很低,所以在使用赋值表达式的时候需要明确的使用括号;
        用户级自定义变量的生命周期是在一个连接中有效,不能用来做连接间的通信;

3.应用场景及注意事项

        应用场景:查询排名

set @rank :=0;
select staff_name,@rank:=@rank+1 as rank from incom_table order by incom desc limit 10;

        注意事项:确认取值顺序,下面的例子证明了优化器优化后的执行顺序。

查询ur_user表中一条数据

因为where和select在查询的不同阶段执行,所以查询到两条记录,不符合预期。
set @usernum:=0;
select user_id,@usernum:=@usernum+1 as usernum from ur_user where @usernum<=1;


引入了order by后,打印出了全部结果,因为order by引入了文件排序,而where条件是在文件排序操作之前取值的。
set @usernum:=0;
select user_id,@rownum:=@rownum+1 as usernum from ur_user where @rownum<=1 order by age;


解决问题的关键在于让变量的赋值和取值发生在执行查询的同一阶段,正确如下。
set @usernum:=0;
select user_id,@usernum as usernum from ur_user where (@usernum:=@usernum+1)<=1;

总结

        本文主要介绍对执行过程的优化及特定类型查询的优化,结合执行计划及前篇对索引的优化结合使用。

猜你喜欢

转载自blog.csdn.net/weixin_49442658/article/details/112478875