为了理解这个问题,我们先从Mysql的架构说起。对于Mysql来说,大致可以分为三层。第一层作为客户端与服务端的连接,连接器负责处理与客户端的连接,以及一些权限认证等。比如客户端连接Mysql服务器的一般用户名和密码,以及对数据库表的执行权限。第二层是核心层。基本上Mysql的大部分核心功能都在这一层,包括查询缓存、解析器、优化器等,比如SQL的解析、优化、索引选择,最后生成执行计划。第三层是存储引擎。Mysql直接调用存储引擎API,通过执行引擎查询数据库中的数据。通过Mysql的层级结构,我们首先可以清楚的了解一条SQL的大概执行流程。首先,客户端向服务器发送建立连接的请求。服务器首先检查querycache是??否命中,命中直接返回,否则继续执行。然后来到解析器,进行语法分析,检查一些系统关键字,检查语法是否合规。然后优化器进行SQL优化,比如如何选择索引,然后生成执行计划。最后执行引擎调用存储引擎API查询数据并返回结果。这是一个很一般的SQL执行过程。接下来,我们将详细描述每个步骤。Querycache如果你看Mysql的官方文档就会知道querycache在5.7.20版本已经被弃用,8.0版本已经被删除。为什么要删,可能是觉得太鸡肋了。我们可以使用命令查看查询缓存是否可用。mysql>显示像'have_query_cache'这样的变量;+--------------------+--------+|变量名|值|+------------------+------+|有_query_cache|YES|+----------------+-------+另外querycache还有一些核心参数。更具体的说明请参考官方文档。query_cache_type:是否启用查询缓存,值为0\1\2,分别对应OFF\ON\DEMAND,ON表示启用查询缓存,但可以通过SELECTSQL_NO_CACHE手动禁用,DEMAND表示只缓存以SELECTSQL_CACHE开头的SQL语句。query_cache_limit:缓存结果大小限制,如果查询结果超过大小,则不缓存,默认为1M大小。query_cache_size:查询缓存分配的内存大小,为1024的整数倍。query_cache_min_res_unit:查询缓存分配内存块的最小单位,默认为4KB。这是为查询缓存分配内存的基本单位。即使查询数据只有1个字节,也会按照最小内存单元大小分配内存空间。SQL解析前,系统会判断查询缓存是否开启。如果启用,缓存中的查询将与传入的查询进行比较。如果完全相同,则直接从缓存中返回。但是需要注意的是,无论大小写、空格还是注释,都会影响缓存命中结果,也就是说必须完全一样!比如下面SQL的大小写不一样,空格太多,就无法命中querycache。从用户中选择*;从用户中选择*;从用户中选择*;parser&preprocessor如果查询缓存未命中,就会进入正常的SQL执行环节。首先,就像我们正常的业务开发一样,第一步是验证参数的规则。Mysql也是如此。解析器将进行词法和语法分析,并根据语法规则对SQL进行验证。比如关键词是否使用正确,或者关键词的顺序是否正确。例如,如果将select写为selct,将orderby写为byorder。如果验证通过,则生成“解析树”。然后预处理器就是根据合法规则进一步验证生成的解析树,比如表名和列名是否存在等等。优化器如果说解析器和预处理器是我们业务逻辑的预验证环节,那么优化器就是真正处理业务逻辑的地方。一条查询SQL可以有N种执行方式。优化器的最终目标是找到最好的执行计划,交给执行引擎执行。但是在实际使用中,我们经常会发现Mysql经常会选择错误的索引。我明明有一个更快的索引,但它没有使用它,这导致查询速度变慢。这是因为Mysql的优化器是基于代价模型的优化器。它只是根据已有的成本计算公式,选择成本最低的执行方式。这种执行方式可能不是最快的,但大多数时候都是这样,优化器的选择比我们自己的更准确。总的来说,这个优化过程过于复杂,流程大致如下图所示。更详细的内容可以参考本书《数据库查询优化器的艺术原理解析与SQL性能》(懒得看,吐槽)。执行引擎的大部分核心任务都已由优化器处理。最后,执行引擎只需要查询数据,并根据生成的执行计划返回即可。这一步比较简单。执行引擎只需要根据执行计划的指令调用存储引擎的API即可。当然,如果这一步可以缓存查询结果,那么在这个阶段就可以缓存查询结果,然后再将结果返回给客户端。总结一张图顶一千字。
