mysql查询性能优化

释放双眼,带上耳机,听听看~!

查询执行路径

在实际开发中,一些数据对实时性要求特别高,或者并不经常使用(可能几天就执行一次或两次),这样就需要把缓冲关了,不管这条SQL语句是否被执行过,服务器都不会在缓冲区中查找该数据,每次都会从磁盘中读取。因为如果实时性要求特别高,缓存中数据可能和磁盘中的就不同步,如果数据不经常使用,被缓存起来,就会占用内存。 在my.ini中的query_cache_type,使用来控制表缓存的。

这个变量有三个取值:0,1,2,分别代表了off、on、demand。 0:表示query cache 是关闭。 1:表示查询总是先到查询缓存中查找,即使用了SQL_NO_CACHE仍然查询缓存,它只是不缓存查询结果而不是不使用查询结果。 2:表示只有在使用了SQL_CACHE后,才先从缓冲中查询数据,仍然将查询结果缓存起来

mysql查询性能优化

mysql查询性能优化

通常来说,查询的生命周期大致可以按照顺序来看:从客户端,到服务器,然后在服务器进行解析,生成执行计划,执行,并返回结果给客户端。其中“执行”可以认为是整个生命周期最重要的阶段,这其中包括了大量为了检索数据到存储引擎的调用以及调用后的数据处理,包括排序、分组等。 解析树将使用MySQL语法规则验证和解析查询。例如,他将验证是否使用错误的关键字,或者使用关键字的顺序是否正确,在或者它还会验证引号是否前后正确匹配。 预处理器则根据一些MySQL规则进一步检查解析树是否合法。例如,这里将检查数据表和数据列是否存在,还会解析名字和别名,看看它们是否有歧义。

查询语句执行顺序

(1)  from (3)  join (2)  on (4)  where (5)  group by (6)  avg,sum…. (7)  having (8)  select (9)  distinct (10) order by (11) top


1
2
3
4
5
6
1SELECT `user`.tenant_id,`tenant`.code,count(1) FROM `user` LEFT JOIN `tenant` ON `user`.tenant_id = `tenant`.id
2WHERE  tenant_id IS NOT NULL   GROUP BY tenant_id
3HAVING count(1) >= 10
4ORDER BY tenant_id DESC;
5
6

所有的查询语句都是从from开始执行的,在执行过程中,每个步骤都会为下一个步骤生成一个虚拟表,这个虚拟表将作为下一个执行步骤的输入。

第一步:首先对from子句中的前两个表执行一个笛卡尔乘积,此时生成虚拟表 vt1(选择相对小的表做基础表)。

第二步:接下来便是应用on筛选器,on 中的逻辑表达式将应用到 vt1 中的各个行,筛选出满足on逻辑表达式的行,生成虚拟表 vt2

第三步:如果是outer join 那么这一步就将添加外部行,left outer jion 就把左表在第二步中过滤的添加进来,如果是right outer join 那么就将右表在第二步中过滤掉的行添加进来,这样生成虚拟表 vt3

第四步:如果 from 子句中的表数目多余两个表,那么就将vt3和第三个表连接从而计算笛卡尔乘积,生成虚拟表,该过程就是一个重复1-3的步骤,最终得到一个新的虚拟表 vt3。

第五步:应用where筛选器,对上一步生产的虚拟表引用where筛选器,生成虚拟表vt4,在这有个比较重要的细节不得不说一下,对于包含outer join子句的查询,就有一个让人感到困惑的问题,到底在on筛选器还是用where筛选器指定逻辑表达式呢?on和where的最大区别在于,如果在on应用逻辑表达式那么在第三步outer join中还可以把移除的行再次添加回来,而where的移除的最终的。

第六步:group by 子句将中的唯一的值组合成为一组,得到虚拟表vt5。如果应用了group by,那么后面的所有步骤都只能得到的vt5的列或者是聚合函数(count、sum、avg等)。原因在于最终的结果集中只为每个组包含一行。

第七步:应用cube或者rollup选项,为vt5生成超组,生成vt6。

第八步:应用having筛选器,生成vt7。having筛选器是第一个也是为唯一一个应用到已分组数据的筛选器。 第九步:处理select子句。将vt7中的在select中出现的列筛选出来。生成vt8。

第十步:应用distinct子句,vt8中移除相同的行,生成vt9。事实上如果应用了group by子句那么distinct是多余的,原因同样在于,分组的时候是将列中唯一的值分成一组,同时只为每一组返回一行记录,那么所以的记录都将是不相同的。

第十一步:应用order by子句。按照order_by_condition排序vt9,此时返回的一个游标,而不是虚拟表。sql是基于集合的理论的,集合不会预先对他的行排序,它只是成员的逻辑集合,成员的顺序是无关紧要的。对表进行排序的查询可以返回一个对象,这个对象包含特定的物理顺序的逻辑组织。这个对象就叫游标。正因为返回值是游标,那么使用order by 子句查询不能应用于表表达式。排序是很需要成本的,除非你必须要排序,否则最好不要指定order by。

第十二步:应用top选项。此时才返回结果给请求者即用户。

如何分析SQL执行情况

mysql查询性能优化

select_type:select查询的类型,主要是区别普通查询和联合查询、子查询之类的复杂查询。

a.SIMPLE:查询中不包含子查询或者UNION

b.查询中若包含任何复杂的子部分,最外层查询则被标记为:PRIMARY

c.在SELECT或WHERE列表中包含了子查询,该子查询被标记为:SUBQUERY

d.在FROM列表中包含的子查询被标记为:DERIVED(衍生)

e.若第二个SELECT出现在UNION之后,则被标记为UNION;若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED f.从UNION表获取结果的SELECT被标记为:UNION RESULT

type:表示MySQL在表中找到所需行的方式,又称“访问类型”,常见类型如下: ALL, index,  range, ref, eq_ref, const, system, NULL。从左到右,性能从最差到最好

a. ALL:Full Table Scan, MySQL将遍历全表以找到匹配的行。 

b. index:Full Index Scan,index与ALL区别为index类型只遍历索引树。

c. range:索引范围扫描,对索引的扫描开始于某一点,返回匹配值域的行。显而易见的索引范围扫描是带有between或者where子句里带有<, >查询。当mysql使用索引去查找一系列值时,例如IN()和OR列表,也会显示range(范围扫描),当然性能上面是有差异的。

d. ref:使用非唯一索引扫描或者唯一索引的前缀扫描,返回匹配某个单独值的记录行。

e. eq_ref:类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件 。

f. const、system:当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量(注:system是const类型的特例,当查询的表只有一行的情况下,使用system)。

g.NULL:MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。

possible_keys:指出MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用。

key:显示MySQL在查询中实际使用的索引,若没有使用索引,显示为NULL。

key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的)。 

ref:表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值。

rows:表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数。

**Extra:包含不适合在其他列中显示但十分重要的额外信息 **

**a. Using index:**该值表示相应的select操作中使用了覆盖索引(Covering Index),直接从索引中过滤不需要的记录并返回命中的结果。说明你的查询语句可以只通过查询索引里的信息就能得到结果。(走索引,直接返回数据,无须过滤)

mysql查询性能优化

mysql查询性能优化

**b. Using where:**从数据表中返回数据,然后过滤不满足条件的记录。这在MySQL服务器层完成,MySQL需要从数据表读出记录然后过滤。 (走索引,根据索引加载数据,过滤数据)

mysql查询性能优化

mysql查询性能优化

c. Using index;Using Where:先从索引中拉取记录,然后根据where过滤数据。(走索引,直接返回数据,过滤数据)

mysql查询性能优化

mysql查询性能优化

Using index condition:Index Condition Pushdown(以下简称ICP)是MySQL在5.6开始查询优化方式。

mysql查询性能优化

mysql查询性能优化

mysql查询性能优化

**e.  Using temporary:**表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询。

f.   Using filesort:MySQL中无法利用索引完成的排序操作称为“文件排序” 。

g.  Using join buffer:该值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。

h.   Impossible where:这个值强调了where语句会导致没有符合条件的行。

i.    Select tables optimized away:这个值意味着仅通过使用索引,优化器可能仅从聚合函数结果中返回一行。

MySQL查询优化tips

**1、Join语句的优化 **

尽可能减少Join语句中的NestedLoop的循环总次数;“永远用小结果集驱动大的结果集”;优先优化NestedLoop的内层循环;保证Join语句中被驱动表上Join条件字段已经被索引。

**2、where子句优化 **

索引列类型是字符串,where时一定用引号括起来,否则索引失效。

mysql查询性能优化

mysql查询性能优化

3、应尽量避免在 where 子句中使用!=或<>操作符,否则引擎将放弃使用索引而进行全表扫描。

mysql查询性能优化

mysql查询性能优化

4、应尽量避免在 where子句中使用or 来连接条件,当or左右查询字段只有一个是索引,该索引失效,导致引擎放弃使用索引而进行全表扫描

mysql查询性能优化

mysql查询性能优化

**5、在查询后面加LIMIT来提前终止查询。 **除非确实需要服务器消除重复的行,否则就一定要使用UNION ALL。没有ALL时,MySQL会给临时表加上DISTINCT关键字,这会导致对整个临时表的数据做唯一性检查。 关联查询时确保ON子句中的列上有索引。确保任何的GROUP BY 和 ORDER BY中的表达式只涉及到驱动表中的列。

给TA打赏
共{{data.count}}人
人已打赏
安全运维

OpenSSH-8.7p1离线升级修复安全漏洞

2021-10-23 10:13:25

安全运维

设计模式的设计原则

2021-12-12 17:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索