【摘要】 一、优化的思路和原则有哪些
1、 优化更需要优化的查询 2、 定位优化对象的性能瓶颈 3、 明确优化的目标 4、 从Explain入手 5、 多使用 profile 6、 永远用小结果集驱动大结果集 7、 尽可能在索引中完成排序 8、 只取出自己需要的字段(Columns) 9、 仅仅使用最有效的过滤条件 10、尽可能避免复杂的join
1、优化更需要优化的查询
…
一、优化的思路和原则有哪些
1、 优化更需要优化的查询
2、 定位优化对象的性能瓶颈
3、 明确优化的目标
4、 从Explain入手
5、 多使用 profile
6、 永远用小结果集驱动大结果集
7、 尽可能在索引中完成排序
8、 只取出自己需要的字段(Columns)
9、 仅仅使用最有效的过滤条件
10、尽可能避免复杂的join
1、优化更需要优化的查询
高并发的低消耗(相对)的查询 对整个系统影响远大于低并发高消耗的查询。
2、定位优化对象的性能瓶颈
在拿到一条需要优化的查询时,我们首先要判断出这个查询的瓶颈到底是IO还是CPU。到底是数据库访问消耗多还是数据的运算(如分组排序)消耗多。
3、明确优化的目标
了解数据库目前整体状态,就能知道数据库所能承受的最大压力,也就是我们知道最悲观状况;
要把握该查询相关的数据库对象信息,我们就能知道最理想和最糟糕状态下需要消耗多少资源;
要知道该查询在应用系统中的地位,我们可以分析出改查询可以占用系统资源的比例,也能够知道该查询的效率对客户的体验影响有多大。
4、从Explain入手
Explain能够告诉你这个查询在数据库中是一个什么样的执行计划来实现的。首先我们需要有个目标,通过不断调整尝试,再借助Explain来验证结果是否满足自己的需求,直到得到预期的结果。
5、永远用小结果集驱动大结果集
很多人喜欢在SQL优化的时候说用“小表驱动大表”,这个说法是不严谨的。因为大表经过where条件过滤后返回的结果集并不一定就比小表所返回的结果集大,这个时候还用大表驱动小表,就会得到相反的性能效果。
这样的结果也非常容易理解,在 MySQL 中的 Join,只有 Nested Loop 一种 Join 方式,也就是MySQL 的 Join 都是通过嵌套循环来实现的。驱动结果集越大,所需要循环的次数就越多,那么被驱动表的访问次数自然也就越多,而每次访问被驱动表,即使需要的逻辑 IO 很少,循环次数多了,总量自然也不可能很小,而且每次循环都不能避免的需要消耗CPU,所以 CPU 运算量也会跟着增加。所以,如果我们仅仅以表的大小来作为驱动表的判断依据,假若小表过滤后所剩下的结果集比大表多很多,结果就是给嵌套循环带来更多的循环次数,反之,所需要的循环次数就会更少,总体 IO 量和 CPU 运算量也会少。而且,就算是非 Nested Loop 的 Join 算法,如 Oracle 中的 Hash Join,同样是小结果集驱动大的结果集才是最优的选择。
所以,在优化 Join Query 的时候,最基本的原则就是“小结果集驱动大结果集”,通过这个原则来减少嵌套循环中的循环次数,达到减少 IO 总量以及 CPU 运算的次数。
6、只取出自己需要的字段(Columns)
对于任何查询,返回的数据都是需要通过网络数据包传输给客户端,如果取出的Column越多,需要传输的数据量自然会越大,不论从网络带宽还是网络传输缓冲区来看,都是一种浪费。
7、仅仅使用最有效的过滤条件
举个例子一个用户表user有id和nick_name等字段,索引是id和nike_name两个索引,下面是两个查询语句
#1
select * from user where id = 1 and nick_name = 'zs';
#2
selet * from user where id = 1
两个查询得到结果是一样的,但是第一个语句用到的索引占用空间是比第二个语句大很多的。占用空间大也代表着要读取的数据量也更多。,也就是说2的查询语句才是最优查询。
8、避免复杂的join查询
我们的查询语句所涉及到的表越多,所需要锁定的资源就越多。也就是说,越复杂的 Join 语句,所需要锁定的资源也就越多,所阻塞的其他线程也就越多。相反,如果我们将比较复杂的查询语句分拆成多个较为简单的查询语句分步执行,每次锁定的资源也就会少很多,所阻塞的其他线程也要少一些。
可能很多人会有疑问,将复杂 Join 语句分拆成多个简单的查询语句之后,那不是我们的网络交互就会更多了吗?网络延时方面的总体消耗也就更大了啊,完成整个查询的时间不是反而更长了吗?是的,这种情况是可能存在,但也并不是肯定就会如此。我们可以再分析一下,一个复杂的查询语句在执行的时候,所需要锁定的资源比较多,可能被别人阻塞的概率也就更大,如果是一个简单的查询,由于需要锁定的资源较少,被阻塞的概率也会小很多。所以 较为复杂的连接查询也有可能在执行之前被阻塞而浪费更多的时间。而且我们的数据库所服务的并不是单单这一个查询请求,还有很多很多其他的请求,在高并发的系统中,牺牲单个查询的短暂响应时间而提高整体处理能力也是非常值得的。优化本身就是一门平衡与取舍的艺术,只有懂得取舍,平衡整体,才能让系统更优。
二、利用 Explain和Profiling
1、Explain使用
各种信息展示
字段 | 说明 |
---|---|
ID | 执行计划中查询的序列号 |
Select_type | 查询类型: DEPENDENT SUBQUERY : 子查询中内层的第一个SELECT,依赖于外部查询结果集; DEPENDENT UNION:子查询中的UNION中从第二个SELECT 开始的后面所有SELECT,同样依赖于外部查询结果集; PRIMARY: 子查询中的最外层查询,不是主键查询; SUBQUERY:子查询内层查询的第一个SELECT,结果不依赖于外部结果集; UNCACHEABLE SUBQUERY:结果集无法缓存的子查询; UNION:UNION语句中第二个SELECT开始的后面所有SELECT,第一个SELECT为PRIMARY UNION RESULT:UNION中的合并结果 |
Table | 所访问的数据库中表名称 |
TYPE | 访问方式: ALL: 全表扫描 const: 常量,最多只有一条记录匹配,由于是常量,所以实际上只需要读一次 eq_ref: 最多只有一条匹配结果,一般是主键或者唯一索引来访问的 index: 全索引扫描 range: 索引范围扫描 ref: jion语句中被驱动表索引的引用查询 system: 系统表,表中只有一行数据 |
Possible_keys | 可能用到的索引 |
Key | 使用的索引 |
Key_len | 索引长度 |
Rows | 估算出来的结果集记录条数 |
Extra | 额外信息 |