第四章 查询性能优化


    最优的表设计+最好的索引+合理的查询设计 是高性能的三个必备条件。

一、为什么查询会慢
    查询的速度快慢,重要的是响应时间,如果把查询看作一个任务,那么它是又很多子任务组成,优化查询就是优化其子任务,要么消除一些子任务、要么减少子任务执行次数、要么让子任务执行更快。
    查询的生命周期大致是:从客户端,到服务器,然后在服务器上进行解析,生成执行计划,执行,并返回结果给客户端。其中‘执行’可以认为是生命周期中最重要的阶段,这其中包含了大量为了检索数据到存储引擎的调用,以及调用后的数据处理,包括排序、分组等。
    完成这些任务时,查询需要在不同地方花费时间,包括网络、CPU计算、生成统计信息和执行计划、锁等待等操作,尤其是向底层存储引擎检索数据的调用操作,这些操作需要在内存中操作,CPU操作以及内存不足会导致I/O操作上消耗时间,根据引擎不同,还可能是产生大量上下文切换以及系统调用。
    每一个消耗大量时间的查询案例中,我们都能看见一些不必要的额外操作,某些操作执行了多次,某些操作执行的太慢。优化查询的目的就是减少和消除这些操作所花费的时间。
    
二、慢查询基础:优化数据访问
    查询效率低最基本的原因就是访问数据量太多,除了某些不可避免的情况,大部分性能低下的查询都可以通过减少访问数据量的方式进行优化。对于低效查询,可以通过下面两步骤分析很有效:
        1.确认应用程序是否在检索大量超过需要的数据,这通常意味着访问了太多的行,但有时也可能是访问太多的列。
        2.确认MYSQL服务器层是否在分析大量超过需要的数据行。

是否向数据库请求了不需要的数据
    1.查询不需要的数据:比如用SELECT语句查询大量的结果,然后再获取前面N行数据关闭结果集。解决方式是查询需要条数N,SQL语句用limit N
    2.多表关联查询返回全部的列:返回多个表的全部数据列是不好的,正确的是只取需要的列。
    3.总是取出全部列:SELECT * 需要考虑是否必要,取出全部列会让优化器无法完成索引覆盖扫描这类优化,还会带来额外I/O、CPU、内存消耗。如果考虑代码复用性,查询全部也是可以考虑的,如果应用程序采用了缓存或别的考虑。可以考虑查询超出需要的数据列。
    4.重复查询相同的数据:不断重复的执行相同的查询,然后每次返回结果都相同,可以考虑将数据缓存起来,这样性能更好。

MYSQL是否在扫描额外的记录
    对于mysql,最简单的查询开销的三个指标如下:响应时间、扫描的行数、返回的行数。这三个数据都会记录到慢查询中,所以查询慢查询是找出扫描行数过多的查询好方法。
    
    1.响应时间:响应时间是个表面的值,响应时间是:服务时间+排队时间。排队时间是指服务器因为等待资源没有真正执行查询的时间,当看到一个响应时间时,需要问问自己这个值是否是个合理的值。
    2.扫描的行数和返回的行数:分析查询时,查看该查询的扫描行数是非常有帮助的,一定程度可以说明查询效率高不高,扫描的行数和查询的行数一般在1:1~10:1之间。
    3.扫描的行数和返回的类型:评估查询开销的时候,需要考虑从表中找出一行数据的成本。在EXPLAIN语句中,type列反应了访问类型。访问类型有很多种,从全表扫描到索引扫描、范围扫描、唯一索引扫描、常数引用等,这些访问是从慢到快的。扫描行数也是从大到小。
        如果查询没有办法找到合适的访问类型,那么解决的最好办法是增加一个合适的索引。一般来说,MYSQL能够使用三种方式应用where查询条件,从好到坏依次为:
            Ⅰ.在索引中使用where条件来过滤不匹配的记录,这是在存储引擎层完成的。
            Ⅱ.使用索引覆盖扫描(Extra列中出现Using index)来返回记录,直接从索引中过滤不需要的记录并返回命中结果。这是在MySQL服务器层完成的,但无需再回表查询记录。
            Ⅲ.从数据表中返回数据,然后过滤不满足条件的记录(Extra列中出现Using where),这是在MySQL服务器层完成的,MySQL需要先从数据表读出记录然后过滤。

三、重构查询的方式
    优化查询,可以转换查询写法,但是性能更好。也可以修改应用代码,以另一种方式完成查询。最终目的是一样的。

    1.一个复杂查询还是多个简单查询:MySQL从设计上让连接和断开都很轻量级,再返回一个小的查询结果方面很高效,把一个复杂的查询分解为多个简单查询是很有必要的。

    2.切分查询:将大查询切分为小查询,每个查询功能一样,只完成一小部分,每次返回一小部分查询结果(分页)

    3.分解关联查询:很多高性能应用都会对关联查询进行分解,原本一条关联查询分解为多个简单查询,这种方式有如下优势:
        Ⅰ.让缓存更高效,很多简单查询可能已被缓存,这样查询会跳过访问数据库,关联查询如果其中一个表发生变化,缓存失效概率较大。
        Ⅱ.查询分解后,执行单个查询可以减少锁的竞争
        Ⅲ.应用层做分解,容易对数据库进行拆分,更容易做到高性能和可扩展。
        Ⅳ.可以减少冗余查询,在应用层做关联,意味着某条记录只需查一次,而数据库关联,则可能还要重复访问一部分数据。
        Ⅴ.这样做相当于在引用中做哈希关联,而不是MySQL的嵌套关联。
    4.

四、查询执行的基础
    当我们向MySQL发送一条请求,MySQL做了什么:

    Ⅰ.客户端发送一条查询给服务器
    Ⅱ.服务器先查询缓存,如果命中,则立刻返回存储在缓存中的结果,否则进入下一阶段。
    Ⅲ.服务器进行SQL解析、预处理、再由优化器生成对应的执行计划。
    Ⅳ.MySQL根据优化器生成的执行计划,调用存储引擎API来执行查询
    Ⅴ.将结果返回客户端

MySQL客户端/服务器通信协议
    在任意时刻,要么是服务器向客户端发送消息,要么是客户端向服务器发送消息,两个动作不能同时执行。这种通信协议简单快速,但是也有限制,一个明显的限制就是没法进行流量控制。
查询缓存
    在解析一个查询语句前,如果查询缓存是打开的,那么MySQL会优先检查这个查询是否命中缓存中的数据。如果查询恰好命中了缓存,那么在返回结果之前MySQL会检查一次用户权限,如果没问题,返回结果给客户端。
查询优化处理
    查询优化器执行一个计划,有下面几个步骤:
    
    Ⅰ.语法解析和预处理:语法解析器先解析语法关键字正确性以及关键字顺序是否正确。预处理器进一步检查解析树是否合法,比如检查表或者列名是否存在,是否歧义。
    Ⅱ.查询优化器:一条查询语句可能有多个执行方式,查询优化器作用是找到最优解。
    Ⅲ.数据和索引的统计信息:MySQL系统架构中,服务器层有查询优化器,却没有保存数据和索引的统计信息。统计信息由存储引擎实现。因为服务层没有统计信息,所以MySQL查询优化器在生成查询的执行计划时,需要向存储引擎获取相应信息。
        包括:每个表或者每个索引有多少个页面、每个表每个索引基数是多少,数据行和索引长度、索引的分布信息等。优化器根据这些信息来选择一个最优的执行计划。
    Ⅳ.执行计划:MySQL不会如其它数据库一样生成查询字节码来执行,而是生成指令树,通过查询引擎执行指令树并返回结果。
查询执行引擎
    在解析和优化阶段,MySQL将生成查询对应的执行计划,MySQL的查询引擎则根据这个计划来完成整个查询
返回结果给客户端
    查询的最后一步是返回结果,即使不需要返回结果集给客户端,MySQL仍然会返回这个查询的一些信息,如查询影响的行数。如果查询可被缓存,这一步还会将结果放在查询缓存中。
    

五、MYSQL查询优化器的局限性

    关联子查询:MySQL的子查询实现很糟,尤其是where条件包含in()的子查询。
    UNION的限制:UNION关键字的实现不好。
    索引合并优化:
    等值传递:某些时候,等值传递会带来额外消耗。
    并行执行:MySQL无法利用多核特性来执行并行查询。
    哈希关联:MySQL不支持哈希关联。
    松散索引扫描:MySQL不支持松散索引扫描,也就是无法按照不连续的方式扫描一个索引。
    最大值最小值优化:对于MIN()、MAX()优化做的并不好
    在同一张表上查询和更新:MySQL不支持对一张表同时查询和更新。

六、优化特定类型的查询
优化COUNT()查询:COUNT可以统计某个列值的数量,也可以统计结果集的行数。统计列值数量时,要求列值是非空的(不统计NULL)。统计结果集行数,使用count(*),它会忽略列直接统计行数。
优化关联查询:1.确保ON或者USING字句的列上有索引。2.确保任何GROUP BY和ORDER BY中的表达式只涉及一个表中的列。3.升级MySQL时需要注意,关联语法、运算符优先级等可能发生变化的地方。
优化子查询:尽量用关联查询代替子查询,如果MySQL5.6以后的版本,可以忽略子查询的建议。
优化GROUP BY 和 DISTINCT:这两种查询都可以使用索引优化,这是也最有效的优化方案。

总结:理解查询是如何执行以及时间都消耗到哪些地方,再加上一些诸如解析和优化过程的知识,可以进一步理解上一章讨论的MySQL如何访问表和索引的内容,从另一个维度帮助读者理解MySQL在访问表和索引时查询和索引的关系。
    优化通常需要三管齐下:不做、少做、快速的做。除了这些基础手段,包括查询、表结构、索引等,MySQL还有一些高级特性可以帮助优化应用,例如分区、分区和索引类似但是原理不同。MySQL还支持查询缓存,它可以帮
    你缓存查询结果,当执行完全相同的查询时,直接使用缓存技术。下一章会介绍这些特性。

猜你喜欢

转载自blog.csdn.net/ws346348183/article/details/89094084