【Mysql】你知道一条查询语句是如何执行的吗?

前言

在默认大家学习了Mysql结构的基础上,我们来深入的解析一下sql语句在mysql中是如何流转和实现的.本文会分select查询语句,和数据修改语句两个方面讲解.

下面我们先来看一下概览图:

接下来我们对照着概览图,一步步的来详细解释各个节点的作用.

连接

需要使用Mysql那么第一步就是要连接Mysql.也就是我们熟悉的连接命令

mysql -h$ip -P$port -u$user -p

连接对应了Mysql的,mysql提供了连接的出口,设置user、password等权限校验,用户管理,都是在上文的connection pool中实现的.

当你连接成功后,可以通过show processlist查看用户情况.如下图所示:

连接后,如果你没有其他后续操作,连接命令(Commond)会设置为为Sleep,当一定时间无操作,会断开连接.这个时间由wait_timeout参数控制,默认是8小时.

由于连接是比较耗时的操作,我们在使用的时候一般也都使用了连接池,使用长连接来避免多次获取连接的消耗.

在使用长连接的时候,我们会发现,Mysql的内存会逐渐变高.这是因为MySQL 在执行过程中临时使用的内存是管理在连接对象里面的,在断开连接的时候才会回收.那么怎么处理这个问题呢?

最简单的方法当然是定期的断开连接进行重连,来进行定期的清理.而Mysql 5.7以上版本,引入了mysql_reset_connection来重新初始化连接资源.此方法不会断连和重连,而是把当前连接初始化为最开始连接的状态.

缓存

在连接建立以后就可以执行sql语句了.

我们在设计业务的时候,在查询缓慢的时候,最经常使用的就是缓存,在mysql内部同样也实现了.

MySQL 拿到一个查询请求后,会先到查询缓存查看之前是不是执行过这条语句.之前执行过的语句及其结果可能会以 key-value 对的形式,缓存在内存中.key 是查询的语句,value 是查询的结果.如果查到,直接返回返回值.如果查不到就会继续后面的执行阶段,执行完成后,执行结果会被存入查询缓存中.

但是既然是缓存,必然也会有缓存相应的问题.比如缓存的更新问题,在数据修改,表结构变更情况下都要清理缓存.对于频繁更新的表来说不是很实用.可以使用按需使用的方式.query_cache_type 设置成 DEMAND来关闭缓存,在需要使用的时候用SQL_CACHE显式指定如下:

mysql> select SQL_CACHE * from T where ID=10;

notes: MySQL 8.0 版本直接将查询缓存的整块功能删掉了

解析器

MySQL解析器由两部分组成词法分析扫描字符流,根据构词规则识别单个单词.

MySQL使用Flex来生成词法扫描程序

在sql/lex.h中定义了MySQL关键字和函数关键字,用两个数组存储语法分析在词法分析的基础上将单词序列组成语法短语,最后生成语法树,提交给优化器

语法分析器使用Bison,在sql/sql_yacc.yy中定义了语法规则.

根据关系代数理论生成语法树

在sql目录下,有许多以sql_开头命名的文件,用于接受语法树,执行不同的查询,如sql_select.cc用于select查询

如上所示分析器主要作用:解析语句,生成语法数,提供给优化器.

检查语句中的关键词,表,字段是否存在. 当解析出语句有问题时会报错You have an error in your SQL syntax如下:

mysql> elect * from t where ID=1;

ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'elect * from t where ID=1' at line 1

优化器

经过了分析器,MySQL知道你要做什么了.但是在开始执行之前,mysql会通过优化器优化执行.

比如在表中有多个索引的情况下,决定使用哪个索引.或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序.

比如执行这样的语句:

mysql> select * from t1 join t2 using(ID) where t1.c=10 and t2.d=20;

既可以先从表 t1 里面取出 c=10 的记录的 ID 值,再根据 ID 值关联到表 t2 ,再判断 t2 里面 d 的值是否等于 20.也可以先从表 t2 里面取出 d=20 的记录的 ID 值,再根据 ID 值关联到 t1.再判断 t1 里面 c 的值是否等于 10.这两种执行方法的逻辑结果是一样的,但是执行的效率会有不同,而优化器的作用就是决定选择使用哪一个方案.

执行器

在优化器觉得好方案后,就到了时间的执行了.

开始执行的时候,会再次判断你对这个表 T 有没有执行查询的权限,如果没有,就会返回没有权限的错误.如下所示:

mysql> select * from T where ID=10;

ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'T'

执行器在执行的时候,会根据mysql设置的引擎,调用对应的这个引擎提供的接口在执行.

小结

本文我们用一条查询语句的流转过程,梳理了一下前文msyql的逻辑过程.下一节我们会根据一条修改语句来查看修改语句下的一些不同点.