过程概览
- 客户端发送一条查询给服务器;
- 服务器检查查询缓存,如果命中了缓存,则立即赶回存储在缓存中的结果。否则进入下一阶段;
- 服务器端进行SQL解析,预处理,再由优化器生成对应的执行计划;
- MySQL根据优化器生成的执行计划,调用存储引擎的API来执行查询;
- 将结果返回给客户端
查询缓存
在解析一个查询语句之前,如果查询缓存是打开的,那么MySQL会优先检查这个查询是否命中查询缓存中的数据。这个检查是通过一个对大小写敏感的哈希查找实现的。查询和缓存中的查询即使只有一个字节不同,那也不会匹配缓存结果,这种情况查询会进入下一个阶段的处理。
如果当前的查询恰好命中了查询缓存,那么在返回查询结果之前MySQL会检查一次用户权限。这仍然是无须解析查询SQL语句的,因为在查询缓存中已经存放了当前查询需要访问的表信息。如果权限没有问题,MySQL会跳过所有其他阶段,直接从缓存中拿到结果并返回给客户端。这种情况下,查询不会被解析,不用生成执行计划,不会被执行。
缓存配置参数:

query_cache_limit: MySQL能够缓存的最大结果,如果超出,则增加qcache_not_cached的值,并删除查询结果
query_cache_min_res_unit: 分配内存块时的最小单位大小
query_cache_size: 缓存使用的总内存空间大小,单位是字节,这个值必须是1024的整数倍,否则MySQL实际分配可能跟这个数值不同(感觉这个应该跟文件系统的blcok大小有关)
query_cache_type: 是否打开缓存 OFF: 关闭,ON: 总是打开
query_cache_wlock_invalidate: 如果某个数据表被锁住,是否仍然从缓存中返回数据,默认是OFF,表示仍然可以返回。
查询管理器
在缓存中没有命中到,则进入MySQL语句的查询管理器中进行处理。
在这里查询语句可以转换成一个快速执行的代码,代码执行的结果被送到客户端管理器。这个多步骤操作过程如下
- 查询首先被解析并判断是否合法
- 然后被重写,去除了无用的操作并且加入预优化部分
- 然后被重写,去除了无用的操作并且加入预优化部分
- 接着被优化以便提升性能,并被转换为可执行代码和数据访问计划。
- 然后计划被编译
- 最后,被执行
这个过程涉及到如下几个组成器件:
查询解析器(Query parser):用于检查查询是否合法
查询重写器(Query rewriter):用于预优化查询
查询优化器(Query optimizer):用于优化查询
查询执行器(Query executor):用于编译和执行查询
语法解析器
首先,MySQL通过关键字将SQL语句进行解析,并生成一棵对应的“解析树”。MySQL解析器将使用MySQL语法规则验证和解析查询。例如,它将验证是否使用错误的关键字,或者使用关键字的顺序是否正确等,再或者它还会验证引号是否能前后正确的匹配。如果查询有错,解析器将拒绝该查询。比如,如果你写成”SLECT …” 而不是 “SELECT …”,那就没有下文了。
解析器还会检查关键字是否使用正确的顺序,比如 WHERE 写在 SELECT 之前会被拒绝。
然后,解析器要分析查询中的表和字段,使用数据库元数据来检查:
- 表是否存在
- 表的字段是否存在
- 对某类型字段的 运算 是否 可能(比如,你不能将整数和字符串进行比较,你不能对一个整数使用 substring() 函数)
接着,解析器检查在查询中你是否有权限来读取(或写入)表。强调一下:这些权限由DBA分配。
查询重写器
在这一步,我们已经有了查询的内部表示,重写器的目标是:
- 预优化查询
- 避免不必要的运算
- 帮助优化器找到合理的最佳解决方案
重写器按照一系列已知的规则对查询执行检测。如果查询匹配一种模式的规则,查询就会按照这条规则来重写。下面是(可选)规则的非详尽的列表:
- 视图合并:如果你在查询中使用视图,视图就会转换为它的 SQL 代码。
- 子查询扁平化:子查询是很难优化的,因此重写器会尝试移除子查询
- 去除不必要的运算符:比如,如果你用了 DISTINCT,而其实你有 UNIQUE 约束(这本身就防止了数据出现重复),那么 DISTINCT 关键字就被去掉了。
- 排除冗余的联接:如果相同的 JOIN 条件出现两次,比如隐藏在视图中的 JOIN 条件,或者由于传递性产生的无用 JOIN,都会被消除。
- 常数计算赋值:如果你的查询需要计算,那么在重写过程中计算会执行一次。比如 WHERE AGE > 10+2 会转换为 WHERE AGE > 12 , TODATE(“日期字符串”) 会转换为 datetime 格式的日期值。
- (高级)分区裁剪(Partition Pruning):如果你用了分区表,重写器能够找到需要使用的分区。
- (高级)物化视图重写(Materialized view rewrite):如果你有个物化视图匹配查询谓词的一个子集,重写器将检查视图是否最新并修改查询,令查询使用物化视图而不是原始表。
- (高级)自定义规则:如果你有自定义规则来修改查询(就像 Oracle policy),重写器就会执行这些规则。
- (高级)OLAP转换:分析/加窗 函数,星形联接,ROLLUP 函数……都会发生转换(但我不确定这是由重写器还是优化器来完成,因为两个进程联系很紧,必须看是什么数据库)。
重写后的查询接着送到优化器。
查询优化器
现在语法树被认为合法的了,并且由优化器将其转化为执行计划。一条查询可以由很多种执行方式,最后都返回相同的结果。优化器的作用就是找到这其中最好的执行计划。</br>
MySQL使用的是“选取-投影-联接”策略进行查询。用一个例子就可以理解: select uid,name from user where gender = 1;
- 这个select 查询先根据where 语句进行选取,而不是先将表全部查询出来以后再进行gender过滤
- 这个select查询先根据uid和name进行属性投影,而不是将属性全部取出以后再进行过滤
- 将这两个查询条件联接起来生成最终查询结果.
MySQL使用基于成本(CBO)的优化器,它将尝试预测一个查询使用某种执行计划的成本,并选择其中成本最小的一个。最早的时候,成本的最小单位是随机读取一个4K数据页的成本,后来成本计算公式变得更加复杂,并且引入了一些“因子”来估算某些操作的代价,如当执行一次where条件比较的成本。可以通过查询当前会话的last_query_cost的值来得知MySQL计算的当前查询的成本。
问题
- 缓存是内存还是硬盘(内存中)
- select->update->select时候,update如何通知缓存(发生更改,缓存立刻失效,写入会导致缓存频繁失效,降低效率)
网友评论