原则:优化更需要优化的SQL
哪些Sql更需要优化?
执行频率更高的
定义Sql的性能瓶颈
是IO引起的问题,还是CPU?是数据访问引起还是数据运算引起?
从哪里入手开始?
执行计划
在MySQL中可以使用EXPLAIN查看SQL执行计划,用法:EXPLAIN SELECT * FROM tb_item
结果说明
SELECT识别符。这是SELECT查询序列号。这个不重要。
表示SELECT语句的类型。
有以下几种值:
1.SIMPLE
表示简单查询,其中不包含连接查询和子查询。
2.PRIMARY
表示主查询,或者是最外面的查询语句。
3.UNION
表示连接查询的第2个或后面的查询语句。
4.DEPENDENT UNION
UNION中的第二个或后面的SELECT语句,取决于外面的查询。
5.UNION RESULT
连接查询的结果。
6.SUBQUERY
子查询中的第1个SELECT语句。
7.DEPENDENT SUBQUERY
子查询中的第1个SELECT语句,取决于外面的查询。
8.DERIVED
SELECT(FROM 子句的子查询)。
表示查询的表。
表示表的连接类型。
以下的连接类型的顺序是从最佳类型到最差类型:
system
表仅有一行,这是const类型的特列,平时不会出现,这个也可以忽略不计。
const
数据表最多只有一个匹配行,因为只匹配一行数据,所以很快,常用于PRIMARY KEY或者UNIQUE索引的查询,可理解为const是最优化的。
eq_ref
mysql手册是这样说的:"对于每个来自于前面的表的行组合,从该表中读取一行。这可能是最好的联接类型,除了const类型。它用在一个索引的所有部分被联接使用并且索引是UNIQUE或PRIMARY KEY"。eq_ref可以用于使用=比较带索引的列。
ref
查询条件索引既不是UNIQUE也不是PRIMARY KEY的情况。ref可用于=或<或>操作符的带索引的列。
ref_or_null
该联接类型如同ref,但是添加了MySQL可以专门搜索包含NULL值的行。在解决子查询中经常使用该联接类型的优化。
上面这五种情况都是很理想的索引使用情况。
index_merge
该联接类型表示使用了索引合并优化方法。在这种情况下,key列包含了使用的索引的清单,key_len包含了使用的索引的最长的关键元素。
unique_subquery
该类型替换了下面形式的IN子查询的ref: value IN (SELECT primary_key FROM single_table WHERE some_expr)
unique_subquery是一个索引查找函数,可以完全替换子查询,效率更高。
index_subquery
该联接类型类似于unique_subquery。可以替换IN子查询,但只适合下列形式的子查询中的非唯一索引: value IN (SELECT key_column FROM single_table WHERE some_expr)
range
只检索给定范围的行,使用一个索引来选择行。
index
该联接类型与ALL相同,除了只有索引树被扫描。这通常比ALL快,因为索引文件通常比数据文件小。
ALL
对于每个来自于先前的表的行组合,进行完整的表扫描。(性能最差)
指出MySQL能使用哪个索引在该表中找到行。
如果该列为NULL,说明没有使用索引,可以对该列创建索引来提高性能。
显示MySQL实际决定使用的键(索引)。如果没有选择索引,键是NULL。
可以强制使用索引或者忽略索引:
显示MySQL决定使用的键长度。如果键是NULL,则长度为NULL。
注意:key_len是确定了MySQL将实际使用的索引长度。
前缀索引
显示使用哪个列或常数与key一起从表中选择行。
显示MySQL认为它执行查询时必须检查的行数。
该列包含MySQL解决查询的详细信息
添加索引的条件:
1)经常作为查询条件(排序)的字段
2)不能经常修改
索引可以提供查询的速度,但并不是使用了带有索引的字段查询都会生效,有些情况下是不生效的,需要注意!
1、不在数据库做计算
禁止使用存储过程、视图、触发器、Event。
在并发量大的情况下,这些功能很可能将数据库拖跨,业务逻辑放到服务层具备更好的扩展性,能够轻易实现“增机器就加性能”.
2、不同类型不可进行比较
如果将不同类型比较,Mysql底层会自动进行数据类型转换,大大影响效率
3、禁止使用反向查询
负向查询条件:NOT、!=、<>、!<、!>、NOT IN、NOT LIKE等,会导致全表扫描
4、尽量不使用LIKE关键字的查询
在使用LIKE关键字进行查询的查询语句中,如果匹配字符串的第一个字符为“%”,索引不起作用。只有“%”不在第一个位置,索引才会生效。
5、联合索引的查询的注意事项
MySQL可以为多个字段创建索引,一个索引可以包括16个字段。对于联合索引,只有查询条件中使用了这些字段中第一个字段时,索引才会生效。
6、使用OR关键字的查询
查询语句的查询条件中只有OR关键字,且OR前后的两个条件中的列都是索引时,索引才会生效,否则,索引不生效。
7、尽量不使用OR关键字
在使用or时,如果条件中有一个不是索引字段,就会导致整个索引失效。
此时,推荐大家使用in 或者 union
8、查询条件中尽量不要使用函数
在条件中使用mysql函数,可能导致索引失效。
大多数的MySQL服务器都开启了查询缓存。这是提高性最有效的方法之一,而且这是被MySQL的数据库引擎处理的。当有很多相同的查询被执行了多次的时候,这些查询结果会被放到一个缓存中,这样,后续的相同的查询就不用操作表而直接访问缓存结果了。
这里最主要的问题是,对于程序员来说,这个事情是很容易被忽略的。因为,我们某些查询语句会让MySQL不使用缓存。请看下面的示例:
// 查询缓存不开启
SELECT username FROM user WHERE signup_date >= CURDATE()
// 开启查询缓存
SELECT username FROM user WHERE signup_date >= 2015-01-12
上面两条SQL语句的差别就是 CURDATE() ,MySQL的查询缓存对这个函数不起作用。所以,像 NOW() 和 RAND() 或是其它的诸如此类的SQL函数都不会开启查询缓存,因为这些函数的返回是会不定的易变的。所以,你所需要的就是用一个变量来代替MySQL的函数,从而 开启缓存。
9、当只要一行数据时使用 LIMIT 1
当你查询表的有些时候,你已经知道结果只会有一条结果,但因为你可能需要去fetch游标,或是你也许会去检查返回的记录数。
在这种情况下,加上 LIMIT 1 可以增加性能。这样一样,MySQL数据库引擎会在找到一条数据后停止搜索,而不是继续往后查少下一条符合记录的数据。
10、关联查询时,永远用小结果集驱动大的结果集
其实这样的结果也非常容易理解,在 MySQL 中的 Join,只有 Nested Loop 一种 Join 方式,也就是MySQL 的 Join 都是通过嵌套循环来实现的。驱动结果集越大,所需要循环的此时就越多,那么被驱动表的访问次数自然也就越多,而每次访问被驱动表,即使需要的逻辑 IO 很少,循环次数多了,总量自然也不可能很小,而且每次循环都不能避免的需要消耗 CPU ,所以 CPU 运算量也会跟着增加。
反之,所需要的循环次数就会更少,总体 IO 量和 CPU 运算量也会少。而且,就算是非 Nested Loop 的 Join 算法,如 Oracle 中的 Hash Join,同样是小结果集驱动大的结果集是最优的选择。
11、只取出自己需要的 Columns;不要select *
任何时候在 Query 中都只取出自己需要的 Columns,尤其是在需要排序的 Query 中。
对于任何 Query,返回的数据都是需要通过网络数据包传回给客户端,如果取出的 Column 越多,需要传输的数据量自然会越大,不论是从网络带宽方面考虑还是从网络传输的缓冲区来看,都是一个浪费。
如果是需要排序的 Query 来说,影响就更大了。在 MySQL 中存在两种排序算法:
一种是在MySQL4.1 之前的老算法,实现方式是先将需要排序的字段和可以直接定位到相关行数据的指针信息取出,然后在我们所设定的排序区(通过参数 sort_buffer_size 设定)中进行排序,完成排序之后再次通过行指针信息取出所需要的 Columns,也就是说这种算法需要访问两次数据。
第二种排序算法是从MySQL4.1 版本开始使用的改进算法,一次性将所需要的 Columns 全部取出,在排序区中进行排序后直接将数据返回给请求客户端。改行算法只需要访问一次数据,减少了大量的随机IO,极大的提高了带有排序的 Query 语句的效率。但是,这种改进后的排序算法需要一次性取出并缓存的数据比第一种算法要多很多,如果我们将并不需要的 Columns 也取出来,就会极大的浪费排序过程所需要的内存。
12、尽量不使用子查询
MySQL从4.1版本开始支持子查询,使用子查询进行SELECT语句嵌套查询,可以一次完成很多逻辑上需要多个步骤才能完成的SQL操作。
子查询虽然很灵活,但是执行效率并不高。
执行子查询时,MYSQL需要创建临时表,查询完毕后再删除这些临时表,所以,子查询的速度会受到一定的影响。
优化:
可以使用连接查询(JOIN)代替子查询,连接查询时不需要建立临时表,其速度比子查询快。另外,join关联的条件,最好也是索引字段!