Mysql随笔-一条sql查询语句是如何执行的
谏言,本人写博客是为了整理学习资料,如果有错误请即使指点,如果装逼请滚犊子。
大体上来说,Mysql分为Server层以及存储引擎层两部分。如图所示:
Server层包括连接器、查询缓存、分析器、优化器、执行器等、涵盖了Mysql的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。而存储引擎层负责数据的存储和提取。架构模式是插件式。
连接器
连接器负责的是跟客户端创建链接、获取权限、维持和管理链接。链接分长链接以及短链接,Mysql在创建链接的时候是非常耗时并且复杂的。所以建议使用长链接。但是使用长链接的时候会导致mysql的内存疯涨。因为mysql会在内存中管理这些长链接,怎么解决这些问题有一些方案,这些方案也要看公司配置以及业务形态做决定。离开业务的猜想都是耍流氓。 1,定期断开长链接,使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开链接,之后要查询再重新链接。 2,如果使用Mysql5.7或者更新版本,可以在每次执行一个比较大的操作后,通过执行mysql_reset_connection来重新初始化链接资源。这个过程不需要重连和重新做权限验证,但是会将链接恢复到刚刚创建完时的状态。
查询缓存
缓存顾名思义是在mysql其实是一个kv形式的存储,在内存中。每次mysql拿到一个查询请求后,会先到缓存里查看。但是大多数情况下不建议用缓存。 因为缓存的失效比较频繁,对一个表更新,这个表上所有查询的缓存都会被晴空。mysql8.0后缓存功能删除了,也就是说再也没有这项功能了。
分析器
如果没有命中缓存,就要开始真正执行语句了。首先mysql要对sql语句做解析,会对语法做校验。
优化器
优化器是在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序。比如你执行下面这样的语句,这个语句是执行两个表的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。 这两种执行方法的逻辑结果是一样的,但是执行的效率会有不同,而优化器的作用就是决定选择使用哪一个方案。优化器阶段完成后,这个语句的执行方案就确定下来了,然后进入执行器阶段。如果你还有一些疑问,比如优化器是怎么选择索引的,我还没弄明白,弄明白也不一定有时间写。
执行器
MySQL 通过分析器知道了你要做什么,通过优化器知道了该怎么做,于是就进入了执行器阶段,开始执行语句。开始执行的时候,要先判断一下你对这个表 T 有没有执行查询的权限,如果没有,就会返回没有权限的错误,如下所示 (在工程实现上,如果命中查询缓存,会在查询缓存返回结果的时候,做权限验证。查询也会在优化器之前调用 precheck 验证权限)。 执行器将遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。
mysql> select * from T where ID=10;
ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'T'
如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。 比如我们这个例子中的表 T 中,ID 字段没有索引,那么执行器的执行流程是这样的:
- 调用 InnoDB 引擎接口取这个表的第一行,判断 ID 值是不是 10,如果不是则跳过,如果是则将这行存在结果集中;
- 调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。
- 执行器将上述遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。至此,这个语句就执行完成了。