上一篇:《第8章-1 查询性能优化-优化数据访问》,接着来了解查询执行的过程,这个对sql执行有个更直观的了解。
查询执行的基础
当希望MySQL能够以更高的性能运行查询时,最好的办法就是弄清楚MySQL是如何优化和执行查询的。一旦理解了这一点,很多查询优化工作实际上就是遵循一些原则让优化器能够按照预想的合理的方式运行。
在这里,是时候回头看看我们前面讨论的内容了:MySQL执行一个查询的过程。根据图8-1可以看到,当向MySQL发送一个请求的时候,MySQL到底做了些什么:
1,客户端给服务器发送一条SQL查询语句。
2,服务器端进行SQL语句解析、预处理,再由优化器生成对应的执行计划。
3,MySQL根据优化器生成的执行计划,调用存储引擎的API来执行查询。
4,将结果返回给客户端。
8-1:查询执行路径
上面的每一步都比想象的要复杂,我们在后续章节中将继续讨论。我们会看到,在每一个阶段,查询处于何种状态。查询优化器是其中特别复杂也特别难理解的部分。还有很多的例外情况,例如,当查询使用绑定变量后,执行路径会有所不同,我们将在下一章讨论这点。
MySQL 主要分为 Server 层和引擎层,Server 层主要包括连接器、查询缓存、分析器、优化器、执行器,同时还有一个日志模块(binlog),这个日志模块所有执行引擎都可以共用,redolog 只有 InnoDB 有。
查询语句的执行流程如下:权限校验(如果命中缓存)--->查询缓存(mysql8.0后就废弃了)--->分析器--->优化器--->权限校验--->执行器--->引擎
MySQL的查询具体过程
当MySQL发送一个查询请求的时候,到底做了什么?
主要分为5个阶段:
1,客户端/服务端通信
2,查询缓存:
3,解析器解析
4,查询优化器
5,执行计划
6,返回结果
阶段一:客户端/服务端通信;
Mysql 客户端和服务端通信方式是采用“半双工”的通信方式。
单双工:指数据传输时不能实现双向通信,只能向一个方向发送数据,一方只能发送另一方就只能接收数据(比如广播);
半双工:指数据传输时可以实现双向通信,但是同一个时刻只能允许数据在一个方向进行传输,通信时一方必须发送或者接收完才能执行一下步的操作(比如:对讲机)
全双工:指数据传输时可以实现双向通信,双方通信时有两条通道,一个通道负责发送,一个通道进行接收,可以同时处理发送和接收数据的功能(比如:电话)
服务端响应客户端请求时,客户端必须接收整个返回结果。因此在实际开发中,应该尽量保持查询简单且只返回必须的数据,这也是查询中尽量避免使用 `SELECT *` 和 `LIMIT` 的原因之一。
查看客户端与服务端的连接状态指令:
Show processlist ;
-- or
show full processlist
阶段二:查询缓存;
当Mysql接收到一条查询语句时,会先去缓存进行查询,如果找到对应查询语句数据就直接把数据返回给客户端。
缓存命中要求:SQL语句完全匹配;比如:select * from staff where staff_id=1 必须要求该条SQL语句完全一致,有差别就不行;
缓存什么时候失效:
1、缓存数据的对应表数据被修改了,那么对应查询语句的缓存信息会失效。
2、缓存满了后剔除对应的数据。
正因为如此,在任何的写操作时,MySQL必须将对应表的所有缓存都设置为失效。如果查询缓存非常大或者碎片很多,这个操作就可能带来很大的系统消耗,甚至导致系统僵死一会儿。而且查询缓存对系统的额外消耗也不仅仅在写操作,读操作也不例外:
1、任何的查询语句在开始之前都必须经过检查,即使这条SQL语句永远不会命中缓存
2、如果查询结果可以被缓存,那么执行完成后,会将结果存入缓存,也会带来额外的系统消耗
基于此,我们要知道并不是什么情况下查询缓存都会提高系统性能,缓存和失效都会带来额外消耗,只有当缓存带来的资源节约大于其本身消耗的资源时,才会给系统带来性能提升。如果系统确实存在一些性能问题,可以尝试打开查询缓存,并在数据库设计上做一些优化,比如:
1、多个小表代替一个大表,注意不要过度设计
2、批量插入代替循环单条插入
3、合理控制缓存空间大小,一般来说其大小设置为几十兆比较合适
4、可以通过SQL_CACHE和SQL_NO_CACHE来控制某个查询语句是否需要进行缓存
最后的忠告是不要轻易打开查询缓存,特别是写密集型应用。如果你实在是忍不住,可以将query_cache_type设置为DEMAND,这时只有加入SQL_CACHE的查询才会走缓存,其他查询则不会,这样可以非常自由地控制哪些查询需要被缓存。
相关指令:
查看缓存状态:
Show variables like ‘query_cathe%’;
开启/关闭缓存:
set query_cache_type =ON/set query_cache_type =OFF;
在SQL中指定不使用缓存:
select SQL_NO_CACHE * from table;
查看缓存命中情况:
Show status like ‘Qcache%’;
阶段三:查询优化处理;
查询处理阶段主要包括解析器对SQL解析处理然后优化SQL生成执行计划;
Parser解析器对SQL解析
通过SQL的关键字将SQL分解成对应规则的数据结构(解析树),然后使用MYSQL的语法验证规则对SQL语法规则进行验证(如关键字、语法、关键字顺序);
Optimizer: 查询优化器
当语法验证通过之后,优化器会将SQL进行分析,通过随机数据的抽选查询成本的计算,最后从众多执行计划中选择一条最优的执行计划;
多数情况下,一条查询可以有很多种执行方式,最后都返回相应的结果。优化器的作用就是找到这其中最好的执行计划。
最优执行计划是根据一些列的统计信息计算得来的,这些统计信息包括:每张表或者索引的页面个数、索引的基数、索引和数据行的长度、索引的分布情况等等。
有非常多的原因会导致MySQL选择错误的执行计划,比如统计信息不准确、不会考虑不受其控制的操作成本(用户自定义函数、存储过程)、MySQL认为的最优跟我们想的不一样(我们希望执行时间尽可能短,但MySQL值选择它认为成本小的,但成本小并不意味着执行时间短)等等。
MySQL的查询优化器是一个非常复杂的部件,它使用了非常多的优化策略来生成一个最优的执行计划:
查询优化器原则
覆盖索引扫描
如:select id from user_info 当id列是索引时,将直接返回索引的数据,而不需要去查询具体的表数据。
等值传播
如:where b>a and a=5 改成 b>5 and a=5;
IN的优化
把or 转成in(),mysql中对in进行了优化,首先对in 里面的参数进行排序,然后会进行二分法匹配优化。
关联查询优化
将可转换的外连查询转为内连。
还有子查询优化,提前终止查询 策略等等。
阶段四:调用查询存储引擎;
查询优化阶段生成了对应的执行计划后,mysql根据对应的执行计划调用存储引擎API 执行查询。
阶段五:返回客户端结果集;
一旦Mysql 查询到一条数据结果集就会向客户端逐步返回数据,这样避免了服务端缓存太多的查询结果,就算没有查询到任何结果集也会向客户端返回一些信息(比如像该查询影响的行数数据);
如果开启了缓存,mysql会同时向缓存里面保存查询结果。
MySQL整个查询执行过程,总的来说分为5个阶段:
阶段一:客户端向MySQL服务器发送一条查询请求
阶段二:服务器首先检查查询缓存,如果命中缓存,则立刻返回存储在缓存中的结果。否则进入下一阶段
阶段三:服务器进行SQL解析、预处理、再由优化器生成对应的执行计划
阶段四:MySQL根据执行计划,调用存储引擎的API来执行查询
阶段五:将结果返回给客户端,同时缓存查询结果
上一篇:《第8章-1 查询性能优化-优化数据访问》
下一篇:《第8章-3 查询性能优化1》