一文读懂一条查询语句的宿命

数据库 MySQL
你知道这条语句在Mysql内部的执行过程吗?本篇就大致介绍查询请求的执行流程,引入了连接器、查询缓存、分析器、优化器、执行器几兄弟的分工合作。

[[420209]]

select * from XXX where id=1;

这应该是n年前入门mysql必用的查询语句了吧,学如逆水行舟,不进则退,这么久过去了,你知道这条语句在Mysql内部的执行过程吗?

我们先来看看司空见惯的Mysql基本架构示意图

可以看到,Mysql可以分为

  • Server层,包括连接器、查询缓存、分析器、优化器、执行器等,除了直接落地到磁盘、和从磁盘提取数据外的所有能力。
  • 存储引擎层,负责了数据的读取和存储,支持InnoDB、MyISAM、Memory等多个存储引擎,默认的是InnoBD。

结构大概讲完了,有没有感觉,Mysql就像一个大型的加工厂,你把原材料,也就是sql语句放进去,然后Mysql就转动起来,从头到尾给你加工,最后得出一款产品,也就是执行结果。

嗯,这个比喻好,确实他妈的像,那么这个加工厂具体做了啥呢?

连接器小妹妹

首先,我们都会使用连接命令连上mysql

mysql -hip-pport -u$user -p

这个时候,出来接待我们的就是连接器小妹妹了

连接器小妹妹负责跟我们进行了典型的TPC握手,然后开始问我们的身份,要拿芝麻开门什么的密码来验证我们是否有权限,如果账户或者密码错误了,则会直接跟我们说

"Access denied for user“

验证通过了,则我们和连接器的连接便成功简历了,可以使用

show processlist

查看连接的状态

其中Sleep状态表明是空闲连接

连接建立后,连接器小妹妹还会告诉我们一些规则,比如

如果连接太长时间没动静,则会自动将连接断开,默认是8个小时

如果连接被断开了,等我吗再次发送请求的时候,连接器小妹妹则会告诉我们

Lost connection to MySQL server during query

那么能改吗?

可以的,只需要我们告诉连接器小妹妹,将wait_timeout的参数修改一下就可以了

听起来都是长连接,那么能进行短连接吗?

可以的,连接器小妹妹说了:

如果你们和我建立长连接的话,连接成功后,你们后续有请求,都会一直使用同一个连接。但是如果是短连接,则每次执行完很少的几次操作后我就要就断开连接了,下次再来你们再建立一次连接吧

我们也可以看到,其实和连接器小妹妹建立连接的过程是挺麻烦的,所以平常我们尽量会减少这个行为,一般都会使用长连接。

但是啊,有时候用着用着,mysql这边内存涨的特别快,这是为什么呢?我特地问了连接器小妹妹,她告诉我

mysql在执行过程中临时使用的内存是管理在连接对象里边的,而这些对象只有在连接断开的时候才释放,所以内存占用越来越大,最后有可能被系统强行干掉了,也就是OOM

丢,这么严重,那么我到底用长连接还是短连接呢?

肯定是长连接啦,别急,还是有解决办法的,我们这边给了两个方案

  • 定期断开长连接,使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开 连接,之后要查询再重连。
  • 如果你用的是MySQL 5.7或更新版本,可以在每次执行一个比较大的操作后,通过执行mysql_reset_connection来重新初始化连接资源。这个过程不需要重连和重新做权限验证, 但是会将连接恢复到刚刚创建完时的状态。

到了这里,连接器小妹妹的活就干完了,后面出来接待我们的是缓存查询小哥哥。

缓存查询小哥哥

缓存查询小哥哥也比较简单,一来就将我们的sql语句拿去做对比,他那边有个字典,用key-value的格式记录着查询过的key和结果,而他的做法也极其简单,只要找到一样的,则将结果给回我们了,只有当找不到记录,才会继续走后面的流程,然后继续记录到字典内。

这个时候,我就很疑惑了,如果实际上数据更新了,但是你的字典还是旧的怎么办呢?

缓存查询小哥哥说了,这个不用担心,字典上会记录着查询记录对应的表,如果对应的表有了更新,我这边就会将记录全部清空掉的。

哦,这样子,那么如果是更新比较频繁的情况,可能你的作用也不大哦哈哈哈

是的,你们可以通过

query_cache_type = DEMAND

将关闭掉的,而且在8.0开始,我已经被移除啦。

说完缓存查询小哥哥流下了悲伤的泪水,是啊,毕竟跟不上时代的技术,被淘汰也是难免的。

缓存查询小哥哥说完,便将另一位小哥叫了出来,这位小哥叫分析器。

分析器小哥哥

分析器小哥哥在拿到sql语句后,就开始自己捣鼓了起来,那么他具体做了啥呢?我们在旁边看捣鼓了很久,其实他就做了两步

第一步,词法分析,就是从SQL 语句中提取关键字,比如:查询的表,字段名,查询条件等等。

第二步,语法规则,就是判断SQL语句是否合乎MySQL的语法。

通过分析器小哥哥做的事情,我也明白了,其实词法分析就是将整个SQL语句拆分成一个个单词,而语法规则则根据MySQL定义的语法规则生成对应的数据结构,并存储在对象结构当中。

举个例子,假设有这样一个SQL语句“select id from XXX”。

先通过词法分析,从左到右逐个字符进行解析

然后再通过语法规则解析,判断输入的SQL 语句是否满足MySQL语法,并且生成语法树,语法树大概是这样的

优化器小姐姐

到了这里,优化器小姐姐已经知道我们要干嘛了 毕竟分析器小哥哥已经帮他分析好了,只是可能我们的语句不够优美,所以才需要知心的优化器小姐姐来做优化。

知心小姐姐怎么做的呢?我们看到小姐姐先将SQL语法树拿了出来,然后就开始工作了起来,不得不说,小姐姐工作的样子是真的好看,那么她具体做了啥呢?

在全程围观的我们,看到了她大概做了两件事

  • 逻辑变化
  • 代价优化

逻辑变化是啥呢?逻辑变换就是在关系代数基础上进行变换,其目的是为了化简,同时保证SQL变化前后的结果一致,也就是逻辑变化并不会带来结果集的变化。

其主要包括以下几个方面:

  • 否定消除:针对表达式“和取”或“析取”前面出现“否定”的情况,应将关系条件进行拆分,从而将外层的“NOT”消除。
  • 等值常量传递:利用了等值关系的传递特性,为了能够尽早执行“下推”运算。“下推”的基本策略是,始终将过滤表达式尽可能移至靠近数据源的位置。
  • 常量表达式计算:对于能立刻计算出结果的表达式,直接计算结果,同时将结果与其他条件尽量提前进行化简。

总结下来就是替换和预处理啦。

代价优化呢?代价优化是用来确定每个表,根据条件是否应用索引,应用哪个索引和确定多表连接的顺序等问题,为了完成代价优化,需要找到一个代价最小的方案。

可以这说,我们要执行的查询都是通过代价优化来计算出来的,最终得出了最小代价计划去执行。

优化好后,接下来就是我们的执行器大哥出马了。

执行器大哥

到了这一步,就要准备执行了,开始执行的时候,执行器大哥会一脸严肃的翻看他的权限宝典

查看我们是否有执行查询的权限,如果没有,则会直接告诉我们

SELECT command denied to user 'root'@'localhost' for table 'XXX'

如果验证没问题,他则会根据表的引擎信息,判断要调用哪种引擎接口

例如SQL:select * from XXX where id=1;

假设 “id“ 字段没有设置索引,就会调用存储引擎从第一条开始查,如果碰到了id是1, 就将结果集返回,没有查找到就查看下一行,重复上一步的操作,直到读完整个表或者找到对应的记录。

执行器还画了个草图解释了他一贯的执行顺序

最后

一条查询命令到了这里就算结束了,大致介绍了查询请求的执行流程,引入了连接器、查询缓存、分析器、优化器、执行器几兄弟的分工合作,最后,我们为即将消失的查询缓存器弟弟默哀三分钟......

本文转载自微信公众号「稀饭下雪」

 

责任编辑:姜华 来源: 稀饭下雪
相关推荐

2021-06-07 08:37:03

SQL 查询语句

2022-11-06 21:14:02

数据驱动架构数据

2022-09-22 09:00:46

CSS单位

2018-09-28 14:06:25

前端缓存后端

2022-02-11 14:43:53

SQL语句C/S架构

2021-08-04 16:06:45

DataOps智领云

2023-12-22 19:59:15

2023-11-21 09:41:00

缓存策略存储

2017-05-04 20:29:12

HTTP服务器TCP

2024-01-03 08:54:17

Kubernetes策略工具

2021-05-18 09:48:58

前端开发架构

2020-12-30 09:05:24

架构微内核系统

2018-10-30 11:10:05

Flink数据集计算

2018-09-29 04:53:37

IoT网关物联网IoT

2022-02-22 09:33:38

LIFO数据结构

2017-05-15 11:10:10

大数据聚类算法

2023-11-20 14:58:30

人工智能AI Agents

2021-02-05 05:26:33

字节ASCII控制

2021-09-04 19:04:14

配置LogbackJava

2017-03-07 15:13:28

Scala偏函数函数
点赞
收藏

51CTO技术栈公众号