MYSQL执行一条SELECT语句的具体流程

昨天CSDN突然抽风 我一个ctrl+z把整篇文章给撤掉了还不能复原 直接心态崩了不想写了 不过这部分果然还是很重要,还是写出来吧

流程图 

这里面总共有两层结构Server层 储存引擎

  • Server 层负责建立连接、分析和执行 SQL。MySQL 大多数的核心功能模块都在这实现,主要包括连接器,查询缓存、解析器、预处理器、优化器、执行器等。另外,所有的内置函数(如日期、时间、数学和加密函数等)和所有跨存储引擎的功能(如存储过程、触发器、视图等。)都在 Server 层实现。
  • 存储引擎层负责数据的存储和提取。支持 InnoDB、MyISAM、Memory 等多个存储引擎,不同的存储引擎共用一个 Server 层。现在最常用的存储引擎是 InnoDB,从 MySQL 5.5 版本开始, InnoDB 成为了 MySQL 的默认存储引擎。我们常说的索引数据结构,就是由存储引擎层实现的,不同的存储引擎支持的索引类型也不相同,比如 InnoDB 支持索引类型是 B+树 ,且是默认使用,也就是说在数据表中创建的主键索引和二级索引默认使用的是 B+ 树索引。

第一步:连接器 

就是用户和MYSQL相连接的过程,会发生三次握手和四次挥手,因为连接是基于TCP协议进行传输的

# -h 指定 MySQL 服务得 IP 地址,如果是连接本地的 MySQL服务,可以不用这个参数;
# -u 指定用户名,管理员角色名为 root;
# -p 指定密码,如果命令行中不填写密码(为了密码安全,建议不要在命令行写密码),就需要在交互对话里面输入密码
mysql -h$ip -u$user -p

 如果 MySQL 服务正常运行,完成 TCP 连接的建立后,连接器就要开始验证你的用户名和密码,如果用户名或密码不对,就收到一个"Access denied for user"的错误,然后客户端程序结束执行。

如果用户密码都没有问题,连接器就会获取该用户的权限,然后保存起来,后续该用户在此连接里的任何操作,都会基于连接开始时读到的权限进行权限逻辑的判断。

所以,如果一个用户已经建立了连接,即使管理员中途修改了该用户的权限,也不会影响已经存在连接的权限。修改完成后,只有再新建的连接才会使用新的权限设置。

可以通过

 SHOW PROCESSLIST;

查看有多少用户连接了MYSQL

比如上图的显示结果,共有两个用户名为 root 的用户连接了 MySQL 服务,其中 id 为 6 的用户的 Command 列的状态为 Sleep ,这意味着该用户连接完 MySQL 服务就没有再执行过任何命令,也就是说这是一个空闲的连接,并且空闲的时长是 736 秒( Time 列)。

空闲连接会一直占用着吗?

当然不是了,MySQL 定义了空闲连接的最大空闲时长,由 wait_timeout 参数控制的,默认值是 8 小时(28880秒),如果空闲连接超过了这个时间,连接器就会自动将它断开。

mysql> show variables like 'wait_timeout';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout  | 28800 |
+---------------+-------+
1 row in set (0.00 sec)

当然,我们自己也可以手动断开空闲的连接,使用的是 kill connection + id 的命令。

长链接和短连接的区别

短连接
连接 mysql 服务(TCP 三次握手)
执行sql
断开 mysql 服务(TCP 四次挥手)// 长连接
连接 mysql 服务(TCP 三次握手)
执行sql
执行sql
执行sql
....
断开 mysql 服务(TCP 四次挥手)

所以就是短连接只有一条SQL语句 长连接有多条SQL语句

同样一个任务我们使用长连接要比使用短连接节省了多次连接和断开MYSQL的过程

但是长连接就一点缺点没有吗?非也

MySQL 在执行查询过程中临时使用内存管理连接对象,这些连接对象资源只有在连接断开时才会释放。如果长连接累计很多,将导致 MySQL 服务占用内存太大,有可能会被系统强制杀掉,这样会发生 MySQL 服务异常重启的现象。

那我们如何解决这种问题吗

断开连接不就完事了嘛

1.定期手动断开连接

2.客户端主动重置连接。MySQL 5.7 版本实现了 mysql_reset_connection() 函数的接口,注意这是接口函数不是命令,那么当客户端执行了一个很大的操作后,在代码里调用 mysql_reset_connection 函数来重置连接,达到释放内存的效果。这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态。

每次学XX底层原理的时候都感觉,底层运行在替我负重前行

第二步:查询缓存 

连接工作结束之后,客户端就可以向MYSQL发送SQL语句了 它首先会解析语句的第一个字段 查看这是一条什么语句 假如说发送的是一个SELECT语句 那么它就会去去查询缓存( Query Cache )里查找缓存数据,看看之前有没有执行过这一条命令,这个查询缓存是以 key-value 形式保存在内存中的,key 为 SQL 查询语句,value 为 SQL 语句查询的结果。

如果查询的语句命中查询缓存,那么就会直接返回 value 给客户端。如果查询的语句没有命中查询缓存中,那么就要往下继续执行,等执行完后,查询的结果就会被存入查询缓存中。

这么一看 这太牛了 这个查询缓存 这不就像dp数组一样可以大量优化运行了吗

其实不然 因为每次对这张表更新数据的时候 这张表的缓存就会被全部清除....啊 也就是说 如果是更新后的数据查不到 只能是未更新的查询过一次的数据 

?那我查它干啥 我直接把上一次的查询结果拿出来用多好

很好 MYSQL也是这么想的 所以MYSQL8.0直接把这个东西给删了

第三步:解析SQL

第一件事:词法分析,MYSQL会分析你这个SQL语句中的关键子 然后形成SQL语法树 这样方便后面模块获取 SQL 类型、表名、字段名、 where 条件等等。 (给MYSQL来一把长难句分析)

第二件事:句法分析 就是看有没有语法错误

这里加粗了语法错误 字段,表,数据不存在那不是语法错误哦

就像JVM中的元数据验证 字节码验证一样

第四步:执行 SQL

对应图中的四块

  • prepare 阶段,也就是预处理阶段;
  • optimize 阶段,也就是优化阶段;
  • execute 阶段,也就是执行阶段;

预处理阶段 

 那肯定在预处理器中,于SELECT而言 预处理就做俩事

1.查看字段,表等数据是否存在

2.把* 替换成全部列

优化阶段

对应的当然是优化器

我们知道MYSQL执行语句的时候贼麻烦 又要判断这个索引又判断那个长度的 就是这一步 要确定一个执行的具体方案

我们可以在查询语句最前面加个 explain 命令,这样就会输出这条 SQL 语句的执行计划,然后执行计划中的 key 就表示执行过程中使用了哪个索引 若果这个key参数是null的话 就说明没有使用索引 也就是说 使用的是效率最差的全表查询

具体来讲 优化器会选择什么方案呢?

SELECT id FROM product WHERE id > 1  AND name LIKE 'i%';

这条查询语句的结果既可以使用主键索引,也可以使用普通索引,但是执行的效率会不同。这时,就需要优化器来决定使用哪个索引了。

这里使用的是覆盖索引,也就是说 同时使用这两个索引 这两个索引上的信息足够满足查询请求,不需要再回到主键索引上去取数据。(查询主键索引的 B+ 树的成本会比查询二级索引的 B+ 的成本大,优化器基于查询成本的考虑,会选择查询代价小的普通索引。)

就是个啥流程呢 首先先在二级索引(name)中 找到匹配的数据 然后二级索引存的数据就是主键id 然后记录id>1的数据 所以就不用再去主键索引了 直接就出结果了

执行阶段 

执行阶段 那肯定是执行器

经历过优化器了 我们已经得到了执行方案 那现在就是正式的执行SQL的时机了

执行的过程中,执行器就会和存储引擎交互了,交互是以记录为单位的。

拢共有三种执行方式:

  • 主键索引查询
  • 全表扫描
  • 索引下推

主键索引查询

从语文的角度分析 他肯定是通过主键索引查询的对吧(?)

我们看这行代码

select * from product where id = 1;

 首先id为主键 肯定为1 再有这个筛选条件是等值 所以一定只会查询一次

执行器第一次查询 调用read_first_record 函数指针指向的函数

这个函数指针被指向为 InnoDB 引擎索引查询的接口(优化器参数为const 有这个参数 储存引擎才能选择对应的执行方式),把条件 id = 1 交给存储引擎,让存储引擎定位符合条件的第一条记录。然后呢储存引擎通过主键索引(b+树)找到id为1的数据

判断1:如果记录是不存在的,就会向执行器上报记录找不到的错误,然后查询结束。如果记录是存在的,就会将记录返回给执行器;

然后如果存在的话 判断2

查看是否符合筛选条件 如果符合发送给客户端,如果不符合则跳过该记录。

执行器查询的过程是一个 while 循环,所以还会再查一次,但是这次因为不是第一次查询了,所以这次会调用 read_record 函数指针指向的函数(区别于上面的read_first_record),因为优化器选择的访问类型为 const(前面提到的这个参数再次出现),这个函数指针被指向为一个永远返回 - 1 的函数,所以当调用该函数的时候,执行器就退出循环,也就是结束查询了

全表扫描

select * from product where name = 'iphone';

第一步不变 还是调用 read_first_record 函数指针指向的函数 但是这次因为是全表扫描(优化器参数为all)

第二步 从第一条记录开始读取,看它是否满足WHERE条件(name = iPhone) 如果不满足跳过,满足把这条记录发给客户端

执行器查询的过程是一个 while 循环,所以还会再查一次,会调用 read_record 函数指针指向的函数,因为优化器选择的访问类型为 all,read_record 函数指针指向的还是 InnoDB 引擎全扫描的接口,所以接着向存储引擎层要求继续读刚才那条记录的下一条记录,存储引擎把下一条记录取出后就将其返回给执行器(Server层),执行器继续判断条件,不符合查询条件即跳过该记录,否则发送到客户端;一直重复上述过程,直到存储引擎把表中的所有记录读完,然后向执行器(Server层) 返回了读取完毕的信息; 执行器收到存储引擎报告的查询完毕的信息,退出循环,停止查询。 

索引下推

它是MySQL 5.6 推出的查询优化策略

select * from t_user  where age > 20 and reward = 100000

假如说我们创建了 对于age和reward的索引

联合索引当遇到范围查询 (>、<) 就会停止匹配,也就是 age 字段能用到联合索引,但是 reward 字段则无法利用到索引(硬记就行 后面可能会更新索引失效的部分)

如果不使用索引下推

第一步还是: server层调用储存引擎 找到满足age>20的第一条记录

第二步:存储引擎根据二级索引的 B+ 树快速定位到这条记录后,获取主键值,然后进行回表操作,将完整的记录返回给 Server 层

第三步:

server再判断reward是否等于100000 如果符合 返回给用户 不符合忽略

第四步:接着拿下一条记录(不需要再次定位记录了 记录之间由链表连接) 存储引擎在二级索引定位到记录后,获取主键值,然后回表操作拿到全部记录数据 判断是否符合

然后三四步循环 直到age>20的记录查询完毕

没有索引下推的时候,每查询到一条二级索引记录,都要进行回表操作,然后将记录返回给 Server,接着 Server 再判断该记录的 reward 是否等于 100000

那么有索引下推的时候呢?

Server 层首先调用存储引擎的接口定位到满足查询条件的第一条二级索引记录,也就是定位到 age > 20 的第一条记录;

存储引擎定位到二级索引后,先不执行回表操作,而是先判断一下该索引中包含的列(reward列)的条件(reward 是否等于 100000)是否成立。如果条件不成立,则直接跳过该二级索引。如果成立,则执行回表操作,将完成记录返回给 Server 层。

Server 层在判断其他的查询条件(本次查询没有其他条件)是否成立(为什么只有reward条件可以用储存引擎判断呢? 因为它虽然没用上联合索引 但是索引的数据还是有的 在联合索引中对着id找reward还是很方便的(一对一对应的)),如果成立则将其发送给客户端;否则跳过该记录,然后向存储引擎索要下一条记录。 如此往复,直到存储引擎把表中的所有记录读完。

就相当于把判断reward是否等于100000这件事外包给储存引擎了

索引下推能够减少二级索引在查询时的回表操作,提高查询的效率,因为它将 Server 层部分负责的事情,交给存储引擎层去处理了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/149.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WebSocket理解

WebSocket理解 WebSocket定义与HTTP关系相同点:不同点&#xff1a;联系总体过程 HTTP问题长轮询Ajax轮询 WebSocket特点 WebSocket 定义 本质上是TCP的协议 持久化的协议 实现了浏览器和服务器的全双工通信&#xff0c;能更好的节省服务器资源和带宽 与HTTP关系 相同点: 基于…

接口测试 react+unittest+flask 接口自动化测试平台

目录 1 前言 2 框架 2-1 框架简介 2-2 框架介绍 2-3 框架结构 3 平台 3-1 平台组件图 1 新建用例 2 生成测试任务 3 执行并查看测试报告 3-2 用例管理 3-2-1 用例设计 3-3 任务管理 3-3-1 创建任务 3-3-2 执行任务 3-3-3 测试报告 3-3-4 邮件通知 1 前言 构建…

【力扣算法12】之 11. 盛最多水的容器 python

文章目录 问题描述示例1示例2提示 思路分析代码分析完整代码详细分析运行效果截图调用示例运行结果完结 问题描述 给定一个长度为 n 的整数数组 height 。有n条垂线&#xff0c;第i条线的两个端点是(i, 0)和(i, height[i])。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构…

解决IDEA项目external libraries依赖包消失的问题

有时候电脑重启后&#xff0c;再打开IDEA上的项目时会出现external libraries目录下的依赖包都消失了的情况&#xff0c;只剩下了一个JDK的包 网上说可以通过刷新IDEA的缓存解决&#xff0c;但我试了没有效果&#xff0c;最后使用如下办法解决&#xff1a; 1.删除项目目录下的…

图论算法笔记

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 第12章 最短路径算法12-1 有权图的最短路径问题最短路径问题-路径规划单源最短路径带权图的最短路径和无权图的最短路径带权图的最短路径算法-Dijkstra算法 12-2 Di…

搭载率突破40%!智能数字座舱比拼,车企还有降本空间吗?

进入2023年&#xff0c;汽车行业的「降本」风潮&#xff0c;驱动产业链上下游开始思考智能化、电动化的投入产出。除了显性的硬件成本&#xff08;继续堆料&#xff0c;强调性价比&#xff0c;还是减配&#xff09;&#xff0c;软件及背后的开发成本&#xff0c;对于车企来说&a…

GitLab 入选 Forrester Wave™️ 集成软件交付平台,并获评唯一「领导者」!

越来越多企业意识到多工具链集成带来的低效和高成本问题&#xff0c;同时承受着可见性低、反馈不畅通、网络风险大等痛点&#xff0c;应用平台方法来交付软件的呼声越来越大。 极狐(GitLab) 很早就意识到了平台方法的价值&#xff0c;也坚信极狐GitLab 这种单一应用程序的 DevS…

学习vue2笔记

学习vue2笔记 文章目录 学习vue2笔记脚手架文件结构关于不同版本的Vuevue.config.js配置文件ref属性props配置项mixin(混入)插件scoped样式总结TodoList案例webStorage组件的自定义事件全局事件总线&#xff08;GlobalEventBus&#xff09;消息订阅与发布&#xff08;pubsub&am…

el-date-picker 日期时间进行限制,精确到时分秒

需求&#xff1a;用户只能选择当时时间或当前时间之前的时间&#xff0c;且精确到时分秒 实现效果&#xff1a;用户只能选择当前时间的时间&#xff0c;如果选择是当天之前的时间&#xff0c;时分秒不做限制&#xff0c;如果选择的是当天时间&#xff0c;就要判断时分秒&#…

【数据挖掘】时间序列教程【十】

5.4 通用卡尔曼滤波 上一节中描述的状态空间模型作为观测方程的更一般的公式 和状态方程 这里是一个p1 向量是一个k1 向量, 是一个pk 矩阵, 是kk 矩阵。我们可以想到的和 给定初始状态 和 &#xff0c;预测方程为&#xff08;类似于上面&#xff09; 并且更新方程是&#x…

华为Harmony应用开发初探

HarmonyOS是一款面向万物互联时代的、全新的分布式操作系统。在传统的单设备系统能力基础上,HarmonyOS提出了基于同一套系统能力、适配多种终端形态的分布式理念,能够支持手机、平板、智能穿戴、智慧屏、车机等多种终端设备,提供全场景(移动办公、运动健康、社交通信、媒体…

外包干了2个月,技术退步明显...

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入湖南某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…

Linux进程(二)---进程的优先级和环境变量

我想在这先完成上一章的一个未说完的话题.最后一个我们讲到了僵尸进程&#xff0c;是指子进程已经结束&#xff0c;但是父进程还在运行没有来得及回收.此时这个子进程便是僵尸进程. 但是如果父进程运行完了&#xff0c;也没有回收就直接结束了&#xff0c;那这个子进程改由谁维…

用html+javascript打造公文一键排版系统3:获取参数设置、公文标题排版

我们用自定义函数setDocFmt()来实现对公文的排版。 一、获取公文参数值 要对公文进行排版&#xff0c;首先要读取公文“参数设置”区中的参数值。比如公文要求对公文标题的一般规定是&#xff1a;一般用2号小标宋体字&#xff0c;居中显示。标题与正文中间空一行。 这些是“参…

Python控制流程盘点及高级用法、神秘技巧大揭秘!

目录 一、条件语句&#xff08;If-Elif-Else&#xff09; 二、循环结构&#xff08;For和While&#xff09; 三、异常处理&#xff08;Try-Except&#xff09; 四、控制流程的高级用法&#xff01; 1. 列表解析 2. 生成器表达式 3. 装饰器 One More Thing&#xff01;&…

Microsoft 宣布今年底关闭开源软件托管平台 CodePlex

Microsoft 宣布&#xff0c;将关闭开源软件托管平台 CodePlex。Microsoft 2006 年推出这项服务&#xff0c;并决定在今年 12 月 15 日将其关闭。 Microsoft 公司副总裁 Brian Harry 在网上博客中写道&#xff0c;人们将可以下载他们的数据档案&#xff0c;Microsoft 正与面向开…

配电柜实时监测?这也太会省力了吧!

现代企业厂房的安全和效率对于业务的成功至关重要。在这个背景下&#xff0c;配电柜监控成为了一项关键的技术。通过实时监测和管理厂房内的配电柜&#xff0c;企业可以确保电力供应的稳定性&#xff0c;提高能源利用效率&#xff0c;并及时发现和解决潜在的故障和安全风险。 配…

matlab学习指南(2):安装工具箱Toolbox的方法(详细图解)

&#x1f305;*&#x1f539;** φ(゜▽゜*)♪ **&#x1f539;*&#x1f305; 欢迎来到馒头侠的博客&#xff0c;该类目主要讲数学建模的知识&#xff0c;大家一起学习&#xff0c;联系最后的横幅&#xff01; 喜欢的朋友可以关注下&#xff0c;私信下次更新不迷路&#xff0…

Uniapp 版本更新

文章目录 前言Uniapp更新确定接口是否能够使用基本代码封装更新软件区别 前言 软件发布之后更新是经常出现的需求。我们希望软件能够自动连网更新软件&#xff0c;而不是重新去手动安装一个apk安装包。不需要更新的软件只有两个&#xff0c;一个是微信小程序&#xff0c;另一个…

openpnp - 汇川 Inovance IS620PS2R8I-IAB-C的参数读取

文章目录 openpnp - 汇川 Inovance IS620PS2R8I-IAB-C的参数读取概述笔记伺服和配套电机型号官方伺服调试软件笔记H00H01H02H03H04H05H06H07H08H09H0AH0BH0CH0DH0FH11H12H16H17H30H31自定义组备注END openpnp - 汇川 Inovance IS620PS2R8I-IAB-C的参数读取 概述 设备中用到了…