MySQL查询性能的优化涉及多个方面,其中包括库表结构、创建合理的索引、设计合理的查询。库表结构包括如何设计表之间的关联、表字段的数据类型等。这须要依据具体的场景进行设计。以下咱们从数据库的索引和查询语句的设计两个角度介绍如何提升MySQL查询性能。sql
索引是存储引擎中用于快速找到记录的一种数据结构。索引有多种分类方式,按照存储方式能够分为:聚簇索引和非聚簇索引;按照数据的惟一性能够分为:惟一索引和非惟一索引;按照列个数能够分为:单列索引和多列索引等。索引也有多种类型:B-Tree索引、Hash索引、空间数据索引(R-Tree)、全文索引等。数据库
在利用B-Tree索引进行查询的过程当中,有几点注意事项,咱们以表A进行说明。其中表A的定义以下:缓存
create table A(id int auto_increment primary key, name varchar(10), age tinyint, sex enum('男','女'), birth datatime, key(name,age,sex)); id为主键,并在name,age,sex列上创建了索引。性能优化
Hash索引与B-Tree的区别:服务器
索引不只仅可让服务器快速定位到表的指定位置,并且还有如下优势:网络
正确地建立和使用索引是实现高性能查询的基础。前面已经介绍了各类类型的索引以及对应的优缺点。高效地选择和使用索引有不少种方式,其中有些是针对特殊案例的优化,有些则是针对特定行为的优化。数据结构
如上是盗取的一个向InnoDB表中插入数据的时间和索引大小的图,其中userinfo表和userinfo_uuid表惟一的区别是userinfo表以id为主键,而userinfo_uuid表以uuid为主键,而插入100万和300万数据的顺序是按照id列的顺序插入的,由上图可知,当插入300万数据行时,userinfo_uuid表因为不是按照主键(uuid)的顺序插入的数据,会致使大量的页分裂,从而插入须要更多的时间、索引占用更大的空间。函数
固然覆盖查询仍是有不少陷阱可能致使没法实现优化的。MySQL查询优化器会在执行查询前判断是否有一个索引可以进行覆盖,覆盖where条件中的字段和select的字段。若是不能覆盖,则仍是须要扫描数据行。性能
由于InnoDB表中非聚簇索引中存储主键值,因此咱们先根据条件获取主键值,而后再根据主键值进行查询,这种方式叫作延迟关联。优化
如上是分别使用主键id排序和name排序的查询,能够看出使用id排序的查询使用了索引排序,而name排序的查询使用的是filesort。
总的来讲编写查询语句时,应尽量选择合适的索引以免单行查找,尽量的使用原生顺序从而避免额外的排序操做,并尽量使用索引覆盖查询。咱们经过响应时间来对查询进行分析,找出消耗时间最长的查询或者给服务器带来压力最大的查询,而后检查查询的schema、SQL和索引结构,判断是否有查询扫描了太多的行,是否作了不少额外的排序或者使用了临时表,是否使用了随机I/O访问数据,或者太多回表查询哪些不在索引中的列的操做。
在发现查询效率不高时,首先就须要考虑查询语句的设计是否合理。以下将会介绍一些查询优化技巧,而后在介绍一些MySQL优化器内部的机制,并展现MySQL是如何执行查询的。最后探索查询优化的模式,以帮助MySQL更有效地执行查询。
查询性能低下的最基本缘由是访问的数据太多了。所以大部分的性能低下查询均可以经过减小访问的数据量进行优化。减小数据访问量一般意味着访问了太多的行,但有时也多是访问了太多的列。在查询时若是仅须要查询结果集中的前某些行,则最简单的方式是在查询语句的最后加上limit。在进行多表关联查询时应尽可能避免使用select *,由于它返回表的全部列,可是这些列可能并不都是必须的。除了请求了不须要的数据,还须要查看MySQL是否在扫描额外的记录,其中能够经过扫描行数和返回行数进行衡量。若是发现查询中须要扫描大量的数据可是只返回少数的行,一般能够:
设计查询的时候一个须要考虑的重要问题是,是否须要将一个复杂的查询分红多个简单的查询。在传统的实现中老是强调数据库层完成尽量多的工做,这样的逻辑在于之前老是认为网络通讯、查询解析和优化是一件代价很高的事情。可是这样的想法对于MySQL并不适用,MySQL从设计上链接和断开链接都很轻量级,在返回一个小的查询结果方面很高效。
分解关联查询:不少高性能的应用都会对关联查询进行分解,简单地说就是对每一个表进行一次单表查询,而后将结果在应用程序中进行关联。以下图所示:
查询计算机1班学生的全部成绩,咱们能够将上过程分解为三个子步骤,以下:
那么这么分解的好处又在哪里呢?首先是让缓存的效率更高。许多应用程序能够方便的缓存单表查询对应的结果对象。如已经缓存了计算机1班对应的id为1,tb_student表中1班的学生有1号和5号,从而能够从成绩表中查询1号和5号学生的成绩;其次查询分解后,执行单个查询能够减小锁竞争;再次查询自己效率也会有所提高。如上使用in()代替关联查询,可让MySQL按照ID顺序进行查询,这可能比随机的关联更加高效;最后分解关联查询能够减小冗余记录的查询,在应用层作关联查询时,意味着对于某条记录应用只须要查询一次,而在数据库中作关联查询,则可能须要重复地访问一部分数据。
当但愿MySQL可以以较高的性能运行查询时,最好的办法就是弄清楚MySQL是如何优化和执行查询的。以下图展现了向MySQL发送一个请求时MySQL具体的操做过程:
第一步是MySQL客户端/服务器通讯,两者之间通讯协议是“半双工”的,也就是说在某一时刻只能有一方在发送数据。在任何一个时刻MySQL链接都有一个状态,该状态表示MySQL当前的工做,经过SHOW FULL PROCESSLIST命令查询状态。其中状态有Sleep、Query、Locked、Analyzing and statistics、Coping to tmp table、Sorting result、Sending data。
第二步是查寻缓存。在解析一个查询语句以前,若是查询缓存是打开的,那么MySQL会优先检查这个查询是否命中查询缓存中的数据。一般是经过一个对大小写敏感的Hash查找实现。若是命中,那么在返回结果前MySQL会检查一次用户权限,该过程无须解析查询SQL语句。若是未命中,则解析SQL语句。
第三步是查询优化处理。包括解析SQL、预处理、优化SQL执行计划,其中出现任何错误都会终止查询。首先,MySQL经过关键字将SQL语句进行解析,并生成一棵对应的“解析树”。查询优化器负责将解析树转化成执行计划,优化器的做用就是找到查询的较优执行计划。MySQL使用基于成本的优化器,它将尝试预测一个查询使用某种执行计划时的成本(SHOW STATUS LIKE 'Last_query_cost'),并选择成本最小的一个。查询优化器是一个很是复杂的部件,它使用了不少优化策略来生成一个最优的执行计划。优化策略分为:静态优化和动态优化。静态优化能够直接对解析树进行分析,并完成优化。例如,优化器能够经过简单的代数变换将where条件转换成另外一种等价形式,静态优化不依赖于特别的数值,如where中带入的常数。静态优化在第一次完成后就一直有效,即便使用不一样的参数重复执行也不会发生变化,能够认为是一种“编译时优化”。动态优化是上下文相关的,如where条件中取值、索引条目对应的数据行数等,是一种“运行时优化”。以下是MySQL可以处理的优化类型:
当MySQL须要对选择的数据进行排序时,若是没法使用索引进行排序,那么MySQL在数据量小则在内存中进行排序,若是数据量大则须要磁盘进行排序,不过MySQL将这一过程统一称为文件排序(filesort)。若是须要排序的数据量小于“排序缓冲区”,MySQL使用内存进行“快速排序”操做,若是内存不够排序,MySQL先对数据进行分块,而后对每一个独立的块使用“快速排序”,并将各块排序结果放入磁盘,而后将各个排好序的块进行合并(merge)。在关联查询的时候若是须要排序,MySQL会分两种状况来处理这样的文件排序,若是order by子句中的全部列都来自关联的第一个表,那么MySQL在关联处理第一个表的时候就进行文件排序,则MySQL的EXPLAIN结果的extra字段就会有“using filesort”。除此以外的其余状况,MySQL都会先将关联结果放到一个临时表中,,而后在全部关联都结束后再进行文件排序,此时的MySQL的EXPLAIN结果的extra字段值为“Using temporary;Using filesort”。若是查询中有limit的话,limit也会在排序以后应用,因此即便返回较少的数据,临时表和须要排序的数量仍会很是大(MySQL5.6的limit子句在此处已经作了改进)。
第四步是查询执行引擎。MySQL根据执行计划给出的指令逐步执行,在该过程当中,有大量的操做须要经过调用存储引擎实现的接口来完成,也就是“Handler API”。MySQL在优化阶段就为每一个表建立一个handler实例,优化器根据这些实例的接口获取表的相关信息。
最后一步就是将查询的结果返回给客户端。MySQL将结果集返回客户端是一个增量、逐步返回的过程。一旦服务器处理完最后一个关联表,开始生成第一条结果时,MySQL就能够开始想客户端逐步返回结果。这样有两个好处:一是服务器端无须存储太多的结果;二是结果集中的每一行都会以一个知足MySQL客户端/服务器通讯协议的封包发送,再经过TCP协议进行传输,从而是客户端能够在第一时间得到返回的结果。
综上全部的内容可知,建立高性能应用程序要考虑schema、索引、查询语句以及查询优化等问题。理解查询是如何被执行的以及时间都消耗在哪些地方,从而针对耗时大的查询语句进行改进。