「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解析器由两部分组成

  1. 词法分析
  • 扫描字符流,根据构词规则识别单个单词.
  • MySQL使用Flex来生成词法扫描程序
  • sql/lex.h中定义了MySQL关键字函数关键字,用两个数组存储
  1. 语法分析
  • 在词法分析的基础上将单词序列组成语法短语,最后生成语法树,提交给优化器
  • 语法分析器使用Bison,在sql/sql_yacc.yy中定义了语法规则.
  • 根据关系代数理论生成语法树

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

如上所示分析器主要作用:

  1. 解析语句,生成语法数,提供给优化器.
  2. 检查语句中的关键词,表,字段是否存在. 当解析出语句有问题时会报错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的逻辑过程.下一节我们会根据一条修改语句来查看修改语句下的一些不同点.

举报
评论 0