mysql笔记03 查询性能优化

查询性能优化算法

1. 为何查询速度会慢?数据库

    1). 若是把查询看做是一个任务,那么它由一系列子任务组成每一个子任务都会消耗必定的时间。若是要优化查询,实际上要优化其子任务要么消除其中一些子任务要么减小子任务的执行次数要么让子任务运行的更快缓存

    2). 一般来讲,查询的生命周期大体能够按照顺序来看:从客户端,到服务器端,而后在服务器上进行解析,生成执行计划,执行,并返回结果给客户端。其中"执行"能够认为是整个生命周期中最重要的阶段,这其中包括性能优化

         大量为了检索数据到存储引擎的调用以及调用后的数据处理,包括排序、分组等。服务器

    3). 在完成这些任务的时候,查询须要在不一样的地方花费时间,包括网络,CPU计算,生成统计信息和执行计划、锁等待(互斥等待)等操做,尤为是向底层存储引擎检索数据的调用操做,这些调用须要在内存中操做、CPU操做网络

         和内存不足时致使的IO操做上消耗时间,根据上下文不一样,可能会产生大量的上下文切换以及系统调用。数据结构

2. 慢查询基础:优化数据访问并发

    查询性能低下最基本的缘由是访问的数据太多。某些查询可能不可避免地须要筛选大量数据,但这并不常见。大部分性能低下的查询均可以经过减小访问的数量的方式进行优化。函数

    对于低效查询,能够经过下面两个步骤来分析:性能

    1). 确认应用程序是否在检索大量超过须要的数据。这一般意味着访问了太多的行,但有时候多是访问了太多的列。

    2). 确认MySQL服务器层是否在分析大量超过须要的数据行。

    2.1 是否向数据库请求了不须要的数据

          1).  一些典型案例

                a. 查询不须要的记录:一个常见的错误是经常会误觉得MySQL会只返回须要的数据,实际上MySQL倒是返回所有结果集在进行计算。最简单有效的解决方法是在这样的查询后面加上LIMIT。

                b. 多表关联时返回所有列

                c. 老是取出所有列:每次看到SELECT * 的时候都须要用怀疑的眼光审视,是否是真的须要返回所有列?取出所有列会让优化器没法完成索引覆盖扫描这类优化,还会为服务器带来额外的网络、IO、内存和

                    CPU的消耗。

                d. 重复查询相同的数据:比较好的方案是,当初次查询的时候将这个数据缓存起来,须要的时候从缓存中取出,这样性能会更好。

    2.2 MySQL是否在扫描额外的记录:

          1). 对于MySQL,最简单的衡量查询开销的三个指标以下:

               a. 响应时间:响应时间是两部分之和:服务时间和排队时间。服务时间是指数据库处理这个查询真正花多长时间。排队时间是指服务器由于等待某些资源而没有真正执行查询的时间--多是等IO操做完成,也可能

                                 是等待行锁等等。

               b. 扫描的行数和返回的行数:分析查询时,查看该查询扫描的行数是很是有帮助的。这在必定程度上可以说明该查询找到须要的数据的效率高不高。

               c. 扫描的行数和访问类型:在评估查询开销的时候,须要考虑一下从表中找到某一行数据的成本。MySQL有好几种访问方式能够查询并返回一行结果。有些方式可能须要扫描不少行才能返回一行结果,也有些访问

                  方式可能无需扫描就能返回结果。

                  在EXPALIN语句中的type列反应了访问类型。访问类型有不少种,从全表扫描到索引扫描、范围扫描、惟一索引扫描、常数引用等。这里列的这些,速度是从慢到快,扫描的行数是从多到少。你不要记住这

                  些访问类型,但须要明白扫描表、扫描索引、范围访问和单值访问的概念。

          2). 通常MySQL能使用以下三种方式应用WHERE条件,从好到坏依次为:

               a. 在 索引 中使用WHERE条件来过滤不匹配的记录。这是在存储引擎层完成的。

               b. 使用索引覆盖扫描(在Extra列中出现Using index)来返回记录,直接从索引中过滤不须要的记录并返回命中的结果。这是在MySQL服务器层完成的,但无需再回表查询记录。

               c. 从数据表中返回数据,而后过滤掉不知足条件的记录(在Extra列中出现Using Where)。这在MySQL服务器层完成,MySQL须要先从数据表读取记录而后过滤。

          3). 若是发现查询须要扫描大量的数据但只返回少数的行(使用聚合函数等),那么一般能够尝试下面的技巧去优化它们:

               a. 使用索引覆盖扫描,把全部须要用的列都放到索引中,这样存储引擎无需回表获取对应行就能够返回结果了。

               b. 改变库表结构。例如使用单独的汇总表。

               c. 重写这个复杂的查询,让MySQL优化器可以以更优的方式执行这个查询。 

3. 重构查询的方式:有时候,能够将查询转换一种写法让其返回同样的结果,但性能更好。

     3.1 一个复杂查询仍是多个简单查询

           a. 设计查询的时候一个须要考虑的重要问题是,是否须要将一个复杂的查询分红过个简单的查询。在传统实现中,老是强调须要数据库层完成尽量多的工做,这样作的逻辑在于之前老是认为网络通讯、

               查询解析和优化是一件代价很高的事情。可是这样的想法对于MySQL并不适用,MySQL从设计上让链接和断开链接都很轻量级,在返回一个小的查询结果方面很高效。现代的网络速度比之前要快的多,

               不管是带宽仍是延迟。

           b. MySQL内部每秒可以扫描内存中上百万行数据,相比之下,MySQL响应数据给客户端就慢得多了。在其余条件都相同的时候,使用尽量少的查询固然是更好的。可是有时候,将一个大查询分解成

               多个小查询也是颇有必要的。

     3.2 切分查询:删除旧数据是一个很好的例子。按期清除大量数据时,若是用一个大的语句一次性删除完成的话,则可能须要一次锁住不少数据、占满整个事务日志、耗尽系统资源、阻塞不少小的但很重要的查询。

           同时须要注意,若是每次删除数据后,都暂停一会再作下一次删除,能够经服务器压力分散到很长的时间段中。

     3.3 分解关联查询:

          分解关联查询的方式重构查询有以下的优点:

          a. 让缓存的效率更高。许多应用程序能够方便地使用缓存单表查询对应的结果集。

          b. 将查询分解后,执行单个查询能够减小锁的竞争。

          c. 在应用层作关联,能够更容易对数据库进行拆分,更容易作到高性能和可扩展性。

          d. 查询自己效率也可能会有所提高。

          e. 能够减小冗余记录的查询。管理查询中可能须要重复地访问一部分数据。

          f. 更进一步,这样作至关于在应用中实现了哈希关联,而不是使用MySQL的嵌套循环关联。某些场景哈希关联的效率要高不少。

4. 查询执行的基础:

    查询执行的过程:

    1). 客户端发送一条查询给服务器

    2). 服务器检查查询缓存,若是命中了缓存,则马上返回存储在缓存中的结果。不然进入下一阶段。

    3). 服务器端进行SQL解析、预处理,再由优化器生成对应的执行计划。

    4). MySQL根据优化器生成的执行计划,调用存储引擎的API来执行查询。

    5). 将结果返回给客户端。 

    4.1 MySQL客户端/服务器通讯协议:MySQL客户端和服务器之间的通讯协议是"半双工"的,这意味着,在任何一个时刻,要么是由服务器向客户端发送数据,要么是由客户端向服务器发送数据,这两个动做不能

          同时发生,因此咱们也没法将一个消息切换成小块独立来发送。

          1). MySQL一般要等待全部的数据都已经发送给客户端才能释放这条查询所占的资源,因此接收所有结果并缓存一般能够减小服务器的压力(?),让查询可以早点结束、早点释放相应的资源。

          2). 查询状态:对于一个MySQL链接,或者说一个线程,任什么时候刻都有一个状态,该状态表示了MySQL当前正在作什么。有不少种方式能查看当前的状态,最简单的是使用SHOW FULL PROCESSLIST命令(

               该命令返回结果中的Command列就表示当前的状态)。下面将这些状态列出来,并作一个简单的解释:

               a. Sleep:线程正在等待客户端发送新的请求。

               b. Query:线程正在执行查询或者正在将结果发送给客户端。

               c. Locked:在MySQL服务器层,该线程正在等待表锁。

               d. Analyzing and statistics : 线程正在收集存储引擎的统计信息,并生成查询的执行计划。

               e. Coping to tmp table [on disk]:线程正在执行查询,而且将其结果都复制到一个临时表中,这种状态通常要么是在作GROUP BY操做,要么是文件排序操做,或者是UNION操做。若是这个状态后面

                   还有"on disk"标记,那表示MySQL正在讲一个内存临时表放到磁盘上。

               f. Sorting result:线程正在对结果集进行排序。

               g. Sending data:这表示多种状况:线程可能在对多个状态之间传输数据,或者而在生成结果集,或者在向客户端返回数据。

     4.2 查询缓存:在解析一个查询语句以前,若是查询缓存时打开的,那么MySQL会优先检查这个查询是否命中查询缓存中的数据。这个检查是经过一个大小写敏感的哈希查找实现的。查询和缓存中的查询即便只有

           一个字节不一样,那也不会匹配缓存结果,这种状况下查询会进入下一个阶段处理。

     4.3 查询优化处理:这个阶段包括多个子阶段:解析SQL、预处理、优化SQL查询计划。这个过程当中任何错误(例如语法错误)均可能终止。

           a. 语法解析器和预处理:MySQL经过关键字将SQL语句进行解析,并生成一颗对应的"解析树",MySQL解析器将使用MySQL语法规则验证和解析查询。预处理则根据一些MySQL规则进一步检查解析树是否合法。

           b. 查询优化器:一条查询语能够有不少执行方式,最后都返回相同的结果。优化器的做用是找到这其中最好的执行计划。MySQL使用基于成本的优化器,它将尝试预测一个查询使用某种执行时的成本,并选择其中

               成本最小的一个。

           1). 有不少种缘由会致使MySQL优化器选择错误的执行计划,以下所示:

                a. 统计信息不许确。MySQL依赖存储引擎提供的统计信息来评估成本,可是有的存储引擎提供的信息时不许确的,有的误差可能很是大。例如:InnoDB由于其MVCC的机构,并不维护一个数据表的行数的精确信息。

                b. 执行计划中的成本估算不等同于实际执行的成本。

                c. MySQL的最优可能和你想的最优不同。你可能但愿执行时间尽量的短,可是MySQL只是基于其成本模型选择最优的执行计划,而有些时候付这并非最快的。

                d. MySQL从不考虑其余并发执行的查询,这可能会影响到当前的查询速度。

                f. MySQL也并非任什么时候候都是基于成本的优化。

                g. MySQL不会考虑不受其控制的操做成本,例如执行存储过程或者用户自定义函数的成本。

                h. 优化器有时候没法去估算全部可能的执行计划,因此他可能错估实际上最优的执行计划。

         2). 优化策略能够简单的分为两种:一种静态优化,一种动态优化。

              a. 静态优化能够直接对解析树进行分析,并完成优化。静态优化在第一次完成后就一直有效,即便使用不一样的参数重复执行也不会发生变化。能够认为这是一种"编译时优化"。

              b. 动态优化则和查询的上下文有关,也可能和不少其余因素有关。例如WHERE条件中的取值、索引中条目对应的数据行数等。须要在每次查询的时候从新评估,能够认为是一种"运行时优化"。

              c. MySQL对查询的静态优化只须要作一次,但对查询的动态优化则在每次执行时都须要从新评估。有时候甚至在查询的执行过程当中也会从新优化。

         3). 下面是一些MySQL可以处理的优化过程:

               a. 从新定义关联表的顺序:

               b. 将外链接转换为内链接

               c. 使用等价变化规则:MySQL可使用一些等价变化来简化并规范表达式。它能够合并和减小一些比较,还能够移除一些恒成立和一些恒不成立的判断。例如:(5=5 ADN a>5) --> a>5。

               d. 优化COUNT(),MIN()和MAX():索引和列是否可为空一般能够帮助MySQL优化这类表达式。例如:要找到某一列的最小值,只须要查询对应B-Tree索引最左端的记录,MySQL能够直接获取索引的最小行。

               e. 预估并转化为常数表达式:当MySQL检测到一个表达式能够转化为常数的时候,就会一直把该表达式做为常数进行优化处理。

               f. 覆盖索引扫描

               g. 子查询优化

               h. 提早终止查询:在发现已知足查询需求的时候,MySQL老是可以马上终止查询。一个典型的例子是使用LIMIT。

               i. 等值传播:USING(film_id)

               j. 列表IN()的比较:在不少数据系统中,IN()彻底等同于多个OR条件的子句,由于这二者是彻底等价的。在MySQL中这点是不成立的,MySQL将IN()列表中的数据先进行排序,而后经过二分查找的方式来肯定

                  列表中的值是否知足条件,这是一个O(log n)复杂度的操做,等价地转换成OR查询的复杂度为O(n),对于IN()列表中有大量取值的时候,MySQL的处理速度将会更快。

         4). 数据和索引的统计信息:由于服务器层没有任何统计信息,因此MySQL查询优化器在生成查询的执行计划时,须要向存储引擎获取相应的统计信息。

         5). MySQL如何执行关联查询:当前MySQL关联执行的策略很简单:MySQL对任何关联都执行嵌套循环操做,即MySQL先在一个表中循环取出单条数据,而后再嵌套循环到下一个表中寻找匹配的行,依次下去,直

               到全部表中匹配的行为止。而后根据各个表匹配的行,返回查询中须要的各个列。MySQL会尝试在最后一个关联表中找到全部匹配的行,若是最后关联表没法找到更多的行之后,MySQL返回到上一层次关联表,

               看是否可以找到更多匹配记录,以此类推迭代执行。

         6). 关联查询优化器:MySQL优化器最重要的一部分就是关联查询优化,它决定了多个表关联时顺序。一般多表关联的时候,能够有多种不一样的关联顺序来得到相同的执行结果。关联查询优化器则经过评估不一样的顺序

              时的成原本选择一个代价最小的关联顺序。优化器会将数据量少的表先进行查询(我的认为第一个查询的表越小,临时表就越小?嵌套查询,减小查询次数?)。

         7). 排序优化:不管如何排序都是一个成本很高的操做,因此从性能角度考虑,应尽量避免排序或者尽量避免对大量数据进行排序。尽可能经过索引进行排序。当不能使用索引生成排序结果的时候,MySQL须要本身

              进行排序,若是数据量小则在内存中进行,若是数量大则须要使用磁盘,不过MySQL将这个过程统一称为文件排序,即便彻底是内存排序不须要任何磁盘文件时也是如此。

              MySQL有以下两种排序算法:

              a. 两次传输排序(旧版本使用):读取行指针和须要排序的字段,对其进行排序,而后再根据排序结果读取所须要的数据行。须要进行两次传输,即须要从数据表中读取两次数据,第二次读取数据的时候,由于是读

                  取排序列进行排序后的全部记录。这回产生大量的随机IO。

              b. 单次传输排序(新版本使用):先读取查询所须要的全部列,而后在根据给定列进行排序,最后直接返回排序结果。效率更高,但占用内存更大。

              若是查询中有LIMIT的话,LIMIT也会在排序以后应用的,因此即便须要返回较少的数据,临时表和须要排序的数据量仍而后很是大。貌似5.6版本有所改进,会先抛弃不知足条件的记录,而后再进行排序。

    4.4 查询执行引擎:在解析和优化阶段,MySQL将生成查询对应的执行计划,MySQL的查询执行引擎则根据这个执行计划来完成整个查询。这里执行计划是一个数据结构,而不是和不少其余的关系型数据库那样会

          生成对应的字节码。 

    4.5 返回结果给客户端:MySQL将结果集返回客户端是一个增量、逐步返回的过程。开始生成第一条结果时,MySQL就开始向客户端逐步返回结果集了。

5. MySQL查询优化器的局限性:

    5.1 关联子查询:MySQL的子查询实现很是糟糕(5.6版本之后有改进)最糟糕的一类查询是WHERE条件中包含IN()的子查询语句。

          1). 由于使用IN()加子查询,性能常常会很是糟,因此一般建议使用EXISTS()等效的改写查询来获取更好的效率。

          2). 通常建议使用左外链接(LEFT OUTER JOIN)代替子查询(?)。

    5.2 UNION的限制:MySQL没法将限制条件从外层"下推"到内层,这使得本来可以限制部分返回结果的条件没法应用到内层查询的优化上。

          例如若是但愿UNION的各个子句可以根据LIMIT只去部分结果集,或者但愿可以先排好序再合并结果集的话,就须要在UNION的各个子句中分别使用这些语句。

          (SELECT first_name,last_name FROM sakila.actor ORDER BY last_name) UNION ALL (SELECT first_name ,last_name FROM sakila.customer ORDER BY last_name) LIMIT 20;

          优化后:

          (SELECT first_name,last_name FROM sakila.actor ORDER BY last_name LIMIT 20) UNION ALL (SELECT first_name ,last_name FROM sakila.customer ORDER BY last_name LIMIT 20) LIMIT 20;

    5.3 当WHERE子句包含多个复杂条件的时候,MySQL可以访问单个表的多个索引以合并和交叉过滤的方式来定位须要查找的行。

    5.4 等值查询:某些时候,等值查询会带来一些意想不到额外消耗。例如:有一个很是大的IN()列表,而MySQ优化器发现存在WHERE、ON或者USING的子句。

    5.5 并行执行:MySQL没法利用多核特性来并行执行查询(貌似5.6之后有改进)。

    5.6 哈希关联:MySQL不支持哈希关联。

    5.7 松散索引扫描:MySQL并不支持松散索引扫描,也就没法按照不连续的方式扫描一个索引。一般,MySQL的索引扫描须要先定义一个起点和终点,即便须要的数据只是这段索引中的不多几个,MySQL仍需扫描这段索引中

          的每个条目。

    5.8 最大值和最小值优化:对于MIN()和MAX()查询,MySQL的优化作的并很差。例如:

          SELECT MIN(actor_id) FROM sakila.actor WHERE first_name='PENELOPE'

          由于first_name上没有索引,因此会进行全表扫描。若是MySQL可以进行主键扫描,那么理论上,当MySQL读到第一个知足条件的记录的时候,就是咱们须要的最小值,由于主键是严格按照actor_id大小字段排序的。

          一个曲线优化的办法是移除MIN(),而后使用LIMIT来将查询重写。

   5.9 在同一个表上查询和更新:MySQL不容许对同一张表同时进行查询和更新。

6. 查询优化器的提示(hint):若是对优化器选择的执行计划不满意,可使用优化器提供的几个提示(hint)来控制最终的执行计划。

7. 优化特定类型的查询

    7.1 优化COUNT()查询

          1). COUNT()是一个特殊的函数,有两种很是不一样的做用:它能够统计某个列值的数量,也能够统计行数。在统计列值的时候要求列值是非空的(不统计NULL)。若是COUNT()的括号中指定了列或者列的表达式,则

               统计的就是这个表达式有值的结果数。最简单的就是咱们使用count(*)的时候,这种状况下通配符*并不会向咱们猜测的那样扩展全部的行,实际上,它会忽略全部的值而直接统计全部的行数。

          2). 使用近似值:有时候某些业务场景并不要求彻底精确的COUNT值,此时能够用近似值来代替。

          3). 更复杂的优化:覆盖索引,增长汇总表等。

    7.2 优化关联查询:

          1). 确保ON或者USING子句中的列上有索引。在建立索引的时候就要考虑到关联的顺序。当表A和表B用到列C关联的时候,若是优化器关联顺序是B、A,那就不须要在B表的对应列上创建索引。没有用到的索引只会

               带来额外的负担。通常来讲,除非有其余理由,不然只须要在关联顺序中的第二个表的相应列上建立索引。

          2). 确保任何的GROUP BY 和ORDER BY中的表达式只涉及到一个表中的列。这样MySQL才有可能使用索引来优化这个过程。

    7.3 优化子查询:关于优化子查询咱们给出的最重要的优化建议就是尽量使用关联查询代替,至少当前MySQL版本须要这样。

    7.4 优化GROUP BY和DISTINCT:

          1). 它们均可以使用索引来优化,这也是最有效的方法。

          2). 在MySQL中,当没法使用索引的时候,GROUP BY使用两种策略来完成:使用临时表或文件排序来作分组。对于任何查询语句,这两种策略的性能都有能够提高的地方。能够经过使用提示SQL_BIG_RESULT和

               SQL_SMALL_RESULT来让优化器按你但愿的方式运行。

          3). 若是须要对关联查询分组(GROUP BY),而且是按照查找表中的某个列进行分组,那么一般采用查找表的标识列分组的效率比其余列更高。

          4). 若是没有经过ORDER BY子句显式地指定排序列,当查询使用GROUP BY 子句的时候,结果集会自动按照分组的列进行排序。若是不关心结果集的顺序,而这中默认排序又致使了须要文件排序,则可使用

               ORDER BY NULL,让MySQL文件再也不进行排序。也能够在GROUP BY子句中直接使用DESC或者ASC关键字,使分组的结果集按照须要的方向排序。

          5). 优化GROUP BY WITH ROLLUP:分组查询的一个变种思想就是要求MySQL对返回的分组结果再作一次超级聚合。最好的办法尽量的将WITH ROLLUP 功能转移到应用程序中处理。

    7.5 优化LIMIT分页:

          1). 使用索引

          2). 要优化这种查询,要么是在页面中限制分页的数量,要么是优化大偏移量的性能。

          3). 尽肯能的使用索引覆盖

          4). 延迟关联

          5). 有时候也能够将LIMIT查询转换为已知位置的查询,让MySQL经过范围扫描找到对应的结果。

          6). 其余优化办法还包括使用预先计算的汇总表,或者关联一个冗余表,冗余表只包含主键列和须要作排序的数据列。

    7.6 优化SQL_CALC_FOUND_ROWS:分页的时候,另外一个经常使用的技巧是在LIMIT语句中加上SQL_CALC_FOUND_ROWS提示(hint),这样就能够得到去掉LIMIT以知足条件的行数,所以能够做为分页的总数。

          用业务的手段解决:下一页,获取更多数据等。

    7.7 优化UNION查询:

          1). MySQL老是经过建立填充临时表的方式来执行UNION查询。所以不少优化策略在UNION查询中都无法很好地使用。常常须要手工地将WHERE,LIMIT,ORDER BY等子句"下推"到UNION的各个子查询中,以

               便优化器能够充分利用这些条件进行优化。

          2). 除非确实须要服务器消除重复的行,不然就必定要使用UNION ALL,这一点很重要。若是没有ALL关键字,MySQL会给临时表加上DISTINCT选项,这回致使对临时表作惟一性检查。这样作的代价很是高,

               即便有ALL关键字,MySQL仍然会使用临时表存储结果。事实上,MySQL老是经结果放入临时表,而后再读出,再返回给客户端。

    7.8 静态查询分析:Percona Toolkit中的pt-query-advisor 可以解析查询日志、分析查询模式,而后再给出全部可能存在的潜在问题的查询,并给出足够详细的建议。这像是给MySQL全部的查询作一次全面的健康

          检查,它能检测出不少问题。

    7.9 用户自定义变量:

8. 通常,咱们要尽可能避免使用SELECT_FOR_UPDATE。不光是队列表,任何状况下都要尽可能避免。

9. 须要处理一种特殊的状况:那些正在被进程处理,而进程自己却因为某种缘由退出的状况。这种状况处理起来很简单。你只须要按期运行UPDATE语句将它都更新成原始状态就能够了,而后执行SHOW PROCESSLIST,

    得到当前正在工做的线程ID,并使用一些WHERE条件避免取到那些刚开始处理的进程。

 

 

 

1. 是否向数据库请求了不须要的数据

    a. 查询不须要的记录:一个常见的错误是经常会误觉得MySQL会只返回须要的数据,实际上MySQL倒是返回所有结果集在进行计算。最简单有效的解决方法是在这样的查询后面加上LIMIT。

    b. 多表关联时返回所有列

    c. 老是取出所有列:每次看到SELECT * 的时候都须要用怀疑的眼光审视,是否是真的须要返回所有列?取出所有列会让优化器没法完成索引覆盖扫描这类优化,还会为服务器带来额外的网络、IO、内存和CPU的消耗。

    d. 重复查询相同的数据:比较好的方案是,当初次查询的时候将这个数据缓存起来,须要的时候从缓存中取出,这样性能会更好。

2. MySQL是否在扫描额外的记录:

    1). 通常MySQL能使用以下三种方式应用WHERE条件,从好到坏依次为:

          a. 在索引中使用WHERE条件来过滤不匹配的记录。这是在存储引擎层完成的。

          b. 使用索引覆盖扫描(在Extra列中出现Using index)来返回记录直接从索引中过滤不须要的记录并返回命中的结果。这是在MySQL服务器层完成的,但无需再回表查询记录。

          c. 从数据表中返回数据,而后过滤掉不知足条件的记录(在Extra列中出现Using Where)。这在MySQL服务器层完成,MySQL须要先从数据表读取记录而后过滤。

    3). 若是发现查询须要扫描大量的数据但只返回少数的行(使用聚合函数等),那么一般能够尝试下面的技巧去优化它们:

          a. 使用索引覆盖扫描,把全部须要用的列都放到索引中,这样存储引擎无需回表获取对应行就能够返回结果了。

          b. 改变库表结构。例如使用单独的汇总表。

          c. 重写这个复杂的查询,让MySQL优化器可以以更优的方式执行这个查询。 

3. 重构查询的方式:切分查询 ,分解关联查询

4. 数据库链接池:预编译

5. 优化特定类型的查询

    1). 优化COUNT()查询

          1). COUNT()是一个特殊的函数,有两种很是不一样的做用:它能够统计某个列值的数量,也能够统计行数。在统计列值的时候要求列值是非空的(不统计NULL)。若是COUNT()的括号中指定了列或者列的表达式,则

               统计的就是这个表达式有值的结果数。最简单的就是咱们使用count(*)的时候,这种状况下通配符*并不会向咱们猜测的那样扩展全部的行,实际上,它会忽略全部的值而直接统计全部的行数。

          2). 使用近似值:有时候某些业务场景并不要求彻底精确的COUNT值,此时能够用近似值来代替。

          3). 更复杂的优化:覆盖索引,增长汇总表等。

    2). 优化关联查询(确保关联查询中可使用索引):

          1). 确保ON或者USING子句中的列上有索引。在建立索引的时候就要考虑到关联的顺序。当表A和表B用到列C关联的时候,若是优化器关联顺序是B、A,那就不须要在B表的对应列上创建索引。没有用到的索引只会

               带来额外的负担。通常来讲,除非有其余理由,不然只须要在关联顺序中的第二个表的相应列上建立索引。

          2). 确保任何的GROUP BY 和ORDER BY中的表达式只涉及到一个表中的列。这样MySQL才有可能使用索引来优化这个过程。

     3). 优化子查询:关于优化子查询咱们给出的最重要的优化建议就是尽量使用关联查询代替,至少当前MySQL版本须要这样。

     4). 优化GROUP BY和DISTINCT:

          1). 它们均可以使用索引来优化,这也是最有效的方法。

          2). 在MySQL中,当没法使用索引的时候,GROUP BY使用两种策略来完成:使用临时表或文件排序来作分组。对于任何查询语句,这两种策略的性能都有能够提高的地方。能够经过使用提示SQL_BIG_RESULT和

               SQL_SMALL_RESULT来让优化器按你但愿的方式运行。

          3). 若是须要对关联查询分组(GROUP BY),而且是按照查找表中的某个列进行分组,那么一般采用查找表的标识列分组的效率比其余列更高。

          4). 若是没有经过ORDER BY子句显式地指定排序列,当查询使用GROUP BY 子句的时候,结果集会自动按照分组的列进行排序。若是不关心结果集的顺序,而这中默认排序又致使了须要文件排序,则可使用

               ORDER BY NULL,让MySQL文件再也不进行排序。也能够在GROUP BY子句中直接使用DESC或者ASC关键字,使分组的结果集按照须要的方向排序。

          5). 优化GROUP BY WITH ROLLUP:分组查询的一个变种思想就是要求MySQL对返回的分组结果再作一次超级聚合。最好的办法尽量的将WITH ROLLUP 功能转移到应用程序中处理。

     5). 优化LIMIT分页:

          1). 使用索引

          2). 要优化这种查询,要么是在页面中限制分页的数量,要么是优化大偏移量的性能。

          3). 尽肯能的使用索引覆盖

          4). 延迟关联

          5). 有时候也能够将LIMIT查询转换为已知位置的查询,让MySQL经过范围扫描找到对应的结果。

          6). 其余优化办法还包括使用预先计算的汇总表,或者关联一个冗余表,冗余表只包含主键列和须要作排序的数据列。

     6). 优化SQL_CALC_FOUND_ROWS:分页的时候,另外一个经常使用的技巧是在LIMIT语句中加上SQL_CALC_FOUND_ROWS提示(hint),这样就能够得到去掉LIMIT以知足条件的行数,所以能够做为分页的总数。

          用业务的手段解决:下一页,获取更多数据等。

     7). 优化UNION查询:

          1). MySQL老是经过建立填充临时表的方式来执行UNION查询。所以不少优化策略在UNION查询中都无法很好地使用。常常须要手工地将WHERE,LIMIT,ORDER BY等子句"下推"到UNION的各个子查询中,以

               便优化器能够充分利用这些条件进行优化。

          2). 除非确实须要服务器消除重复的行,不然就必定要使用UNION ALL,这一点很重要若是没有ALL关键字,MySQL会给临时表加上DISTINCT选项,这回致使对临时表作惟一性检查。这样作的代价很是高,

               即便有ALL关键字,MySQL仍然会使用临时表存储结果。事实上,MySQL老是经结果放入临时表,而后再读出,再返回给客户端。

     8).  静态查询分析:Percona Toolkit中的pt-query-advisor 可以解析查询日志、分析查询模式,而后再给出全部可能存在的潜在问题的查询,并给出足够详细的建议。这像是给MySQL全部的查询作一次全面的健康

           检查,它能检测出不少问题。

5. 是否使用查询缓存,以及怎么使用查询缓存。多插入时,禁用查询缓存

6. 批量插入

相关文章
相关标签/搜索