面试资料

IT面试

http://www.mianwww.com/html/category/company-list/sina

2016十家公司前端面试小记

http://www.cnblogs.com/xxcanghai/p/5205998.html

http://www.kanzhun.com/gsm3146c7.html?sortMethod=1&result=0&q=

hash算法

http://dev.yesky.com/261/35374761.shtml

http://m.oschina.net/blog/194326

高性能MySQL-3rd-(六)查询性能优化

zhmsong 发布于 2年前,共有 0 条评论

/* *  --------------------------------------------------------            
 *     高性能MySQL-3rd-Baron Schwartz-笔记             
 *     第六章 查询性能优化    
 */  --------------------------------------------------------

 

======================================================

    6.1 为什么查询速度会变慢

    查询有生命周期大致顺序:从客户端,到服务器,然后在服务器上进行解析,生成执行计划,执行,返回结果给客户端,其中执行是最重要的阶段。包括了大量的检索数据到存储引擎的调用,调用后的数据处理,分组和排序。

    查询在每个部分都会花费时间,包括网络、CPU计算、生成执行计划、锁等待(互斥等待),尤其是向存储引擎调用操作,这些调用需要在内存操作、在CPU操作、内存不足时导致I/O操作。

    了解以上就可能知道查询速度会变慢的思考角度。

======================================================

    6.2 慢查询基础:优化数据访问

    优化数据访问,就是优化访问的数据,操作对象是要访问的数据,两方面,是否向服务器请求了大量不需要的数据,二是是否逼迫MySQL扫描额外的记录(没有必要扫描)。

    请求不需要数据的典型案例:不加LIMIT(返回全部数据,只取10条)、多表关联Select * 返回全部列(多表关联查询时*返回多个表的全部列)、还是Select *(可能写程序方面或代码复用方面有好处,但还要权衡)、重复查询相同数据(真需要这样,可以缓存下来,移动开发这个很有必要本地存储)。

    标志额外扫描的三个指标:响应时间(自己判断是否合理值)、扫描的行数、返回的行数,一般扫描行数>返回行数。

    扫描的行数需要与一个“访问类型”概念关联,就是 Explain 中的 type,explain的type结果由差到优分别是:ALL(全表扫描)、index(索引扫描)、range(范围扫描)、ref(唯一索引查询 key_col=xx)、const(常数引用)等。从“访问类型”可以明白,索引让 MySQL 以最高效、扫描行数最少的方式找到需要的记录。

    书中有个例子,说明在where中使用已是索引的列和取消该列的索引后两种结果,type由ref变为All,预估要访问的rows从10变为5073,差异非常明显。

======================================================

    6.3 重构查询的方式

    第一:将一个复杂查询拆分为数个小且简单的查询,数据返回也快。

    第二:切分查询,如删除10万条数据,可以切分为10次,每次删除1万条。

    第三:分解关联查询:

    以上做法好处是,充分利用前一步缓存,减少锁竞争,in(123, 456,...)也更高效,减少冗余记录,等等。

======================================================

    6.4 查询执行的基础(知识)

    MySQL执行查询执行路径,如下图,关键要解释的是 客户端发送请求,如果查询缓存有结果,则直接返回。

   

 

    客户端/服务器通讯协议,具体细节不关注,只知道它是“半双工”工作,要么客户端向服务器发送数据,要么服务器向客户端发送数据,两个动作不会同时发生;另外,发送数据都只有发送完成后才能动作,这就是为什么要加LIMIT。

    另外,注意当客户端从服务器获取数据时,看起来是从服务器获取数据,实际上是从库函数的缓存中获取数据,想想PHP的 mysql_query(),此时数据已经到了PHP的缓存中,而mysql_unbuffered_query()不会缓存结果。

    MySQL中的关联(join)查询,总体来说,MySQL认为任何一个查询都是一次关联,不光是查询两个表匹配才叫关联。所以,理解MySQL如何执行关联查询至关重要。MySQL的关联是:嵌套循环关联,举例如下:

 

    结合书中关于多表关联的案例,参考一个实际例子 《MySQL SQL优化之 STRAIGHT_JOIN》 全面介绍优化过程。

    排序优化,不管怎么样,从性能角度,应该尽可能避免排序,或者尽可能避免对大量数据进行排序。第三章讲了索引排序,快速,当不能直接使用索引时,MySQL就会自己进行排序,数据量小时在内存中排序,数据量大时使用到磁盘。量小于“排序缓冲区”时,MySQL使用内存进行“快速排序”。如果内存不够,MySQL先将数据分块,每块使用快速排序,然后将各块结果放在硬盘上,然后合并(merge),最后返回排序结果。

    注意:MySQL排序过程统称为文件排序(filesort),概念上的,即使排序发生在内存,而不是磁盘文件中。

    MySQL有两种排序算法,两次传输排序(旧版)、单次传输排序(新版)。两种各有各的最好和最差的应用场景,注意 max_length_for_sort_data 是临界值,不超过时使用单次传输,超过使用两次传输。MySQL自动判断,具体参考第八章中“文件排序优化”。

    两次传输排序(旧版),读取行指针和需要排序的字段,对其进行排序,然后再根据排序结果读取所需要的数据行。显然是两次传输,特别是读取排序后的数据时(第二次)大量随机I/O,所以两次传输成本高。

    单次传输排序(新版),一次读取出所有需要的或SQL查询指定的列,然后根据排序列,排序,直接返回排序后的结果。顺序I/O,缺点:如果列多,额外占用空间。

    注意:MySQL排序时使用的空间比想象大很多,为什么?因为MySQL要为每一个排序记录分配足够长的空间存放,VARCHAR满长度(声明的完整长度),使用UTF8字符集时,为每个字符预留3个字节。所以会很大!

    结合关联查询,排序会更复杂。如果ORDER BY排序列都在第一个表(驱动表),那么在关联处理时,先对驱动表排序,Explain结果中Extra会有Using filesort;除此之外所有情况,都会在关联结束后,将结果放在临时表中进行最终排序,Extra中会有Using temporary;Using filesort。如果还有LIMIT,也会在排序后应用。可以,排序需要的空间大!

    注意:MySQL5.6以后,有所优化,如果有LIMIT会只排序需要的,而不是所有,抛弃不满足条件的结果。

   查询执行引擎,相对于查询优化,查询执行简单些了,MySQL只根据执行计划输出的指令逐步执行。指令都是调用存储引擎的API来完成,一般称为 handler API,实际上,MySQL优化阶段为每个表都创建了一个 handler 实例,(类似于VC++编程中的句柄?),用 handler 实例获取表的信息(列名、索引统计信息等)。

    注意:存储引擎接口不丰富,底层仅几十个,但功能丰富!如某接口实现了查询第一行,又有一个接口实现了查询下一行,有了这两个就可以全表扫描了!

    返回结果给客户端,有结果集返回结果集,没结果,返回影响的行数。一般MySQL也会将这个结果缓存下来,存放到查询缓存中。

    注意:MySQL返回结果是一个增量、逐步返回的过程,例如,关联操作中,当一个嵌套循环处理到最后一个关联表,并开始生成第一条结果时,MySQL就可以开始向客户端逐步返回结果集了。好处:服务器端无须存储太多结果,也不会因为返回的结果太多而消耗太多内存,也使客户端第一时间获得返回结果。结果是以TCP协议封包发送的,TCP的传输过程,可能会对封包进行缓存然后批量发送。

======================================================

    6.5 MySQL查询优化器的局限性

    不熟悉 JOIN USING 和 JOIN ON 的请看 红薯作品 MySQL 三种关联查询的方式: ON vs USING vs 传统风格

    一个是关联子查询,没看明白,回来再读。

    一个UNION限制,无法将限制条件从外层下推到内层,改造例子如下

 

    等值传递:讲的IN列表,MySQL会将IN列表的值传到各个过滤子句,如果IN列表太大,会造成额外消耗,优化和执行都很慢。

    并行执行,MySQL无法执行并行查询,不用白费力气了。

    哈希关联,MySQL不支持哈希关联,所有关联都是嵌套循环关联。

    松散索引扫描,MySQL不支持松散(跳跃),仍需要扫描每一个条目。

    最大值和最小值,MySQL对 MIN()和MAX()做得不好。看一个例子,强制使用索引来优化(use index(xx))。

    在同一个表上查询和更新,MySQL不允许这样。

======================================================

    6.6 查询优化器的提示(hint)

    讲到了很多提示,意在如果我们对优化器选择的执行计划不满意,使用提示来控制最终的执行计划,如上面的 USE INDEX(PRIMARY),其他还有:HIGH_PRIORITY、LOW_PRIORITY、DELAYED、STRAIGHT_JOIN(上文提到过)、SQL_SMALL_RESULT、SQL_BIG_RESULT、SQL_BUFFER_RESULT、SQL_CACHE、SQL_NO_CACHE、SQL_CALC_FOUND_ROWS、FOR UPDATE、LOCK IN SHARE MODE、USE INDEX、IGNORE INDEX、FORCE INDEX等等。

======================================================

    6.7 优化特定类型的查询

    6.7.1 优化 COUNT()查询

    COUNT()常被误解(难道这本书里说的对的?),COUNT()有两个作用,1、统计非NULL列的列植的数量,2、统计返回数据集的行数;常用的是COUNT(*),*常被误解为所有列,实际上在操作时是忽略所有列,而直接统计所有行数。COUNT(*)中的*与SELECT *中的*是不同的。如果你真想统计结果集的行数,就用 COUNT(*)而不要使用 COUNT(aCol)。

   通常以为 MyISAM执行COUNT(*)最快,实际上是有条件的,只有不用 WHERE时,因为MySQL根本不用扫描数据行,也无须去计算,会直接利用存储引擎的特性去获得这个值。当带上 WHERE 上,就需要去扫描去计算了。

   书中一个优化的例子,将条件反转后可大大加速,如查询 id > 5 的数量有4097行,而反转,查询 id < 5 的,只有几行,然后 用总行数(用 COUNT(*) 获取-常数不费计算)减去 id < 5的,大大优化。但这种情况貌似我提前可以知道 id > 5的数据比 id < 5 的数据多很多才可以。

   能使用近似值的就不必追求精确计算值,代价太高!

   6.7.2 优化关联查询

   这个话题基本整本书都在讨论(还是很晕),注意一下:

   1)确保ON或USING子句中的列上有索引,在创建索引时就要考虑到关联的顺序。

 

原文地址:https://www.cnblogs.com/T8881/p/5211530.html