mysql查询语句底层的执行流程

引入

  • 我去查询T表中 ID为10的数据,我们通常只是看到得到一条结果。其中的过程还不太清楚,如何找到这条数据,先做的什么再做的什么?看到的只是输入一条语句,返回一个结果,却不知道这条语句在 MySQL 内部的执行过程。
mysql> select * from t where id=10
  • 这里把MYSQL拆解下,看看它的组成,经过这个拆解的过程更深入的理解Mysql,当碰到Mysql一些异常和问题,能够看到本质,快速定位与解决。

层层刨析MYSQL基本架构

  • 下图MySQL 的基本架构示意图,从中可以清楚地看到 SQL 语句在 MySQL 的各个功能模块中的执行过程。
    在这里插入图片描述

MySQL 可以分为 Server 层和存储引擎层两部分。

  • Server 层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。
  • 存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始成为了默认存储引擎。
  • 执行 create table 建表的时候,如果不指定引擎类型,默认使用的就是 InnoDB。通过指定存储引擎的类型来选择别的引擎,比如在 create table 语句中使用 engine=memory, 来指定使用内存引擎创建表。不同存储引擎的表数据存取方式不同,支持的功能也不同。如:InnoDB 支持事务而MyISAM不支持等.
  • 从图中不难看出,不同的存储引擎共用一个 Server 层,也就是从连接器到执行器的部分。下面结合开头提到的那条 SQL 语句,带走一遍整个执行流程,依次看下每个组件的作用。

SQL语句执行过程

连接器

  • 第一步,你会先连接到这个数据库上,这时候接待你的就是连接器。连接器负责跟客户端建立连接、获取权限、维持和管理连接。连接命令:
mysql -h$ip -P$port -u$user -p
  • 输完命令之后,你就需要在交互对话里面输入密码。虽然密码也可以直接跟在 -p 后面写在命令行中,但这样可能会导致你的密码泄露。如果你连的是生产服务器,强烈建议你不要这么做。
  • 连接完成后,如果你没有后续的动作,这个连接就处于空闲状态,你可以在 show processlist 命令中看到它。其中的 Command 列显示为“Sleep”的这一行,就表示现在系统里面有一个空闲连接。
    在这里插入图片描述

客户端太长时间没有动静,连接器会自动断开连接,断开连接后客户端在发生请求会报错: Lost connection to MySQL server during query。需要重新连接才可以执行请求.这个自动断开的时间可以通过参数wait_timeout 控制的,默认值是 8 小时

  • 到这里你可能会想到这个时间设置大一点就好了.确实可以这样做,也建议这样做.因为建立连接的过程很复杂尽量使用长连接.
  • 全部使用长连接以后你又会发现,mysql的内存涨得特别快,因为mysql在执行过程中临时使用的内存是管理在连接对象里面的,这些资源只有在连接断开才会释放.so长链接可能会导致内存占用太大,被系统强行杀掉(OOM),从表象来看就是Mysql异常重启了.

怎么解决这个问题呢?可以考虑以下两种方案。
1.定期断开长连接,使用一段时间就去程序里判断执行一个占用内存大的连接,断开连接,之后的查询在进行重连.
2. mysql5.7及以上版本,在每次执行较大操作后,执行mysql_reset_connection 来重新初始化连接资源。这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态。

查询缓存

  • 连接建立完成后,可以执行 select 语句。执行逻辑第二步:查询缓存。
  • MySQL 拿到一个查询请求后,会先查询缓存,看看以前有没有执行过这条语句,执行过的语句及其结果会以key-value的形式直接缓存在内存中,key是查询的sql语句,value是查询的结果。如果查询语句在key中匹配上那么就直接返回value。
  • 如果没有在匹配上查询语句的key,那么就会继续后面的执行阶段,执行完成以后将查询语句和结果缓存起来。这样下次查询的效率将会很高!

但是大多数情况不建议使用缓存查询,因为弊大于利

因为缓存查询失效非常频繁,对于这个表的变动都会导致这个表的所有缓存都被清空。所以你可能刚刚缓存起来还没有使用就被一个更新操作清空了,对于更新压力大的数据库来说,查询缓存的命中率非常低,除非业务使用一张静态表,很长时间才更新一次!比如,一个系统配置表,那这张表上的查询比较适合使用查询缓存。
Mysql提供了"按需使用的方式" 你需要就去查询缓存,不需要就走执行流程。将参数 query_cache_type 设置成 DEMAND,这样默认的sql都不会走缓存,当你有某个sql需要走缓存查询时用 SQL_CACHE 显式指定走缓存查询 如:select SQL_CACHE * from user where id=1

  • 注意: mysql 8.0版本已经直接将查询缓存的整块功能删掉了。

分析器

  • 没有走缓存查询就开始正式的执行语句了,分析器对sql进行解析,看看你需要做什么。
  • 分析器先做"词法解析",sql是由多字符串和空格组成,MySQL需要识别里面的字符串分别是什么,代表什么。

例如:select * from user where id=1
识别到关键字 SELECT 知道是查询sql 识别user成为user表 识别 id为列id

  • 识别以后,分析器开始做"语法分析",根据词法解析的结果,语法分析器会根据语法规则,判断这个sql是否符合MySQL的语法。

如果语法不对就会收到“You have an error in your SQL syntax”的错误提示。

优化器

  • 虽然经过了分析器之后MySQL知道我们需要做什么了,但是在执行前还需要优化器帮我们提高效率。
  • 优化器在表中存在多个索引的时候,决定使用哪个索引。或者在多表join时决定各表连接顺序。
  • 优化器阶段完成后,这个语句的执行方案就确定下来了,然后进入执行器阶段。

执行器

  • MYSQL 通过分析器知道了你要做什么、通过优化器知道了该怎么做就进入到执行器阶段,开始执行语句。
  • 在开始执行的时候,要判断一下你对这个表有没有执行操作的权限,如果没有就会返回没有权限的错误。(如果是开始命中查询缓存的话,会在返回缓存结果的时候做权限验证)
  • 报错如下
mysql> select * from t where id=10;
ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 't'
  • 如果有权限就打开表继续执行,打开表的时候,执行器会根据表的引擎定义,去使用这个引擎提供的接口。
  • 比如 例子中的表t中 id没有索引,执行器的流程是这样的

1、调用InnoDB引擎接口取这个表的第一行,判断id的值是不是10,如果不是则跳过,如果是就将这一行存在结果集中。
2、调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。
3、执行器将满足的条件的行数据组合成结果集返回给客户端。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

甜甜掉在星星上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值