MySql一条查询语句的执行流程是怎么样的?

MySql一条查询语句的执行流程是怎么样的?

1.前言

一条sql语句到底在执行时经历了什么?探究这个问题是学习mysql的重要步骤,面试时常被问到,也使得学习mysql时也有了知识框架的支撑,明白我们背的知识点到底用在哪里,笔者觉得这一点还是很重要的。

注:对一个知识点的总结不仅包含知识点本身,还包含对该知识点的联想,这个联想是在面试时可能被追问的,也可以自己主动说出来(我还知道。。。)加分的。

2.知识点

MySQL 执行流程是怎样的?

首先要知道的是,我们可以把mysql分成两层,server层和数据库引擎层,前者主要是对我们的查询进行处理(主要包括 {连接器},{查询缓存}、{解析器}、{预处理器、优化器、执行器} 等),后者是数据真正存储的地方(从 MySQL 5.5 版本开始, InnoDB 成为了 MySQL 的默认存储引擎)。

一条查询的执行流程如下:

第一步:通过连接器连接 MySQL 服务

mysql -h$ip -u$user -p

连接器联想1\]: 连接经过TCP 三次握手,断开经过四次挥手 \[连接器联想2\]: 如果用户密码都没有问题,连接器就会获取该用户的权限,然后保存起来,后续该用户在此连接里的任何操作,都会基于连接开始时读到的权限进行权限逻辑的判断,意思是管理员修改已登录用户的权限需要等他重新登录才生效 \[连接器联想3\]: 如何查看 MySQL 服务被多少个客户端连接了?`show processlist` \[连接器联想4\]: 空闲连接会一直占用着吗?MySQL 定义了空闲连接的最大空闲时长,由 `wait_timeout` 参数控制的,默认值是 8 小时(28880秒),如果空闲连接超过了这个时间,连接器就会自动将它断开。 \[连接器联想5\]: MySQL 的连接数有限制吗?最大连接数由 max_connections 参数控制,超过这个值,系统就会拒绝接下来的连接请求,并报错提示"Too many connections"。 \[连接器联想6\]: 怎么解决长连接占用内存的问题?MySQL 的连接也跟 HTTP 一样,有短连接和长连接的概念,长连接的好处就是可以减少建立连接和断开连接的过程,但是,使用长连接后可能会占用内存增多,因为 MySQL 在执行查询过程中临时使用内存管理连接对象,这些连接对象资源只有在连接断开时才会释放。有两种解决方式。第一种,**定期断开长连接** 。第二种,**客户端主动重置连接** 。MySQL 5.7 版本实现了 `mysql_reset_connection()` 函数的接口来重置连接,达到释放内存的效果。这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态。 \[连接器联想7\]: 连接器的工作?与客户端进行 TCP 三次握手建立连接;校验客户端的用户名和密码,如果用户名或密码不对,则会报错;如果用户名和密码都对了,会读取该用户的权限,然后后面的权限逻辑判断都基于此时读取到的权限; ##### 第二步:查询缓存 连接器得工作完成后,客户端就可以向 MySQL 服务发送 SQL 语句了,MySQL 服务收到 SQL 语句后,就会解析出 SQL 语句的第一个字段,看看是什么类型的语句。 如果 SQL 是查询语句(select 语句),MySQL 就会先去查询缓存( Query Cache )里查找缓存数据。 但是其实**查询缓存挺鸡肋**的。对于更新比较频繁的表,查询缓存的命中率很低的,因为只要一个表有更新操作,那么这个表的查询缓存就会被清空。 所以,MySQL 8.0 版本直接将**server层**查询缓存删掉了。 ##### 第三步:解析SQL 在正式执行 SQL 查询语句之前, MySQL 会先对 SQL 语句做解析,这个工作交由「解析器」来完成。 解析器会做两件事情:**词法分析** 、 **语法分析**。 \[解释器联想1\]: 词法分析:MySQL 会根据你输入的字符串识别出关键字出来,例如,SQL语句 select username from userinfo,在分析之后,会得到4个Token,其中有2个Keyword,分别为select和from. \[解释器联想2\]: 语法分析:根据词法分析的结果,语法解析器会根据语法规则,判断你输入的这个 SQL 语句是否满足 MySQL 语法,如果没问题就会构建出 SQL 语法树,这样方便后面模块获取 SQL 类型、表名、字段名、 where 条件等等。 \[解释器联想3\]: 解如果我们输入的 SQL 语句语法不对,就会在解析器这个阶段报错。(释器的主要作用) \[解释器联想4\]: 解释器只负责检查语法和构建语法树,但是不会去查表或者字段存不存在。 ##### 第四步:执行 SQL 解析SQL无误后,执行SQL需要经过三个步骤:预处理器、优化器、执行器。 ###### 预处理器 * 检查 SQL 查询语句中的表或者字段是否存在; * 将 `select *` 中的 `*` 符号,扩展为表上的所有列; ###### 优化器 优化器主要负责将 SQL 查询语句的执行计划确定下来,比如在表里面有多个索引的时候,优化器会基于查询成本的考虑,来决定选择使用哪个索引。 \[优化器联想1\]: 要想知道优化器选择了哪个索引,我们可以在查询语句最前面加个 `explain` 命令,这样就会输出这条 SQL 语句的执行计划。`explain select * from product where id = 1` \[优化器联想2\]: 一般来讲普通索引查询效率高于主键索引,当索引覆盖时会先考虑普通索引的B+树上查询,这就是执行计划,是优化器决定的。 ###### 执行器 确定了执行计划,接下来 MySQL 就真正开始执行语句了,在执行的过程中,执行器就会和存储引擎交互了,交互是以记录为单位的。 * 主键索引查询 `select * from product where id = 1;` 让InnoDB引擎通过主键索引B+树搜索id=1的记录。 * 全表扫描 `select * from product where name = 'iphone';` 查询条件没有用到索引,触发全表扫描,查询每一条记录判断是否满足条件。 * 索引下推 (MySQL 5.6 推出的查询优化策略) \[索引下推联想1\]: 索引下推能够减少**二级索引** 在查询时的回表操作,提高查询的效率,因为它将 Server 层部分负责的事情,交给存储引擎层去处理了。`select * from t_user where age > 20 and reward = 100000;`不使用索引下推(MySQL 5.6 之前的版本)时,定位到 age \> 20 的一条记录,获取主键值,然后**进行回表操作** ,将完整的记录返回给 Server 层,Server 层再判断该记录的 reward 是否等于 100000。而使用索引下推后,判断记录的 reward 是否等于 100000 的工作交给了存储引擎层:定位到 age \> 20 的第一条记录,存储引擎定位到二级索引后,**先不执行回表** 操作,而是先判断一下该索引中包含的列(reward列)的条件(reward 是否等于 100000)是否成立。如果**条件不成立** ,则直接**跳过该二级索引** 。如果**成立** ,则**执行回表**操作,将完成记录返回给 Server 层。 ##### MySQL 执行流程是怎样的?总结: (总结只是简单总结,也就是被问到时该说的,上面的知识点,是可能被追问时涉及的,或者自己说出来的加分项。) * 连接器:建立连接,管理连接、校验用户身份; * 查询缓存:查询语句如果命中查询缓存则直接返回,否则继续往下执行。MySQL 8.0 已删除该模块; * 解析 SQL,通过解析器对 SQL 查询语句进行词法分析、语法分析,然后构建语法树,方便后续模块读取表名、字段、语句类型; * 执行 SQL:执行 SQL 共有三个阶段: * 预处理阶段:检查表或字段是否存在;将 `select *` 中的 `*` 符号扩展为表上的所有列。 * 优化阶段:基于查询成本的考虑, 选择查询成本最小的执行计划; * 执行阶段:根据执行计划执行 SQL 查询语句,从存储引擎读取记录,返回给客户端;

相关推荐
观无7 分钟前
.NET-EFCore基础知识
数据库·.net
^_^ 纵歌10 分钟前
mongodb和clickhouse比较
数据库·clickhouse·mongodb
旅行的橘子汽水1 小时前
【C语言-全局变量】
c语言·开发语言·数据库
pwzs1 小时前
缓存不只是加速器:深入理解 Redis 的底层机制
数据库·redis·缓存
A尘埃1 小时前
电商中的购物车(redis的hash类型操作)
数据库·redis·哈希算法
程序员学习随笔2 小时前
PostgreSQL技术内幕28:触发器实现原理
数据库·postgresql
在下千玦2 小时前
#关于数据库中的时间存储
数据库
寰宇视讯2 小时前
铼赛智能Edge mini斩获2025法国设计大奖 | 重新定义数字化齿科美学
前端·数据库·edge
三金C_C2 小时前
数据库预热
数据库
小费的部落7 小时前
记 etcd 无法在docker-compose.yml启动后无法映射数据库目录的问题
数据库·docker·etcd