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

news2024/11/23 19:06:28

昨天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.coloradmin.cn/o/717925.html

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

相关文章

PF-Net点云补全解读

论文链接&#xff1a;https://arxiv.org/pdf/2003.00410.pdfhttps://arxiv.org/pdf/2003.00410.pdf PF-Net是一个点云补全模型&#xff0c;整体网络为生成对抗网络&#xff08;Gan&#xff09;构成&#xff0c;G网络负责生成缺失的数据&#xff0c;D网络负责鉴别G网络生…

为什么Unity使用AssetBundle热更的时候要剔除掉.mainfest文件

1&#xff09;为什么Unity使用AssetBundle热更的时候要剔除掉.mainfest文件 ​2&#xff09;Addressable热更&#xff0c;断点续传问题 3&#xff09;在线性空间&#xff0c;使用后处理解决UI混合问题 这是第341篇UWA技术知识分享的推送&#xff0c;精选了UWA社区的热门话题&am…

计算机毕业论文内容参考|基于Python的手机订餐系统的设计与实现

文章目录 导文摘要前言绪论1课题背景2国内外现状与趋势3课题内容相关技术与方法介绍系统分析系统设计系统实现系统测试总结与展望1本文总结2后续工作展望导文 计算机毕业论文内容参考|基于Python的手机订餐系统的设计与实现 摘要 本文基于Python语言,设计并实现了一个手机订餐…

我的毕业故事

前言 2023年6月25日&#xff0c;在拿到双证(毕业证书/学士学位证书)之后&#xff0c;我正式毕业&#xff0c;结束了本科学习阶段。 由于毕业设计中了盲审&#xff0c;因此在大致顺利的过程中平添了不少故事&#xff0c;这里就对近两个月做一个回顾。 盲审 首先解释一下什么…

MySQL之binlog、redolog、undolog

1.binlog是 mysql server 层的一种二进制日志&#xff0c;用来记录数据库的写入操作&#xff0c;并以"事务"的形式保存在磁盘上&#xff0c;主要的使用场景有主从复制和数据恢复。 2.redo log &#xff08;重做日志&#xff09;是InnoDB存储引擎产生的&#xff0c;记…

day30-Oracle函数

Oracle函数 1.oracle字符函数 1.1 拼接函数concat 1.2 首字母转大写initcap 1.3 lower转小写&#xff1b;upper转大写 1.4 trim去除首尾空格 1.5 substr截取字符串3,4,3代表从第三个字符开始&#xff0c;4代表共截取4个字符 1.6 replace替换字符串 2.oracl…

局部均方差滤波(磨皮)

note 锚点灰度值 用 原图像对应点的原灰度值 和 局部均值加权得到 局部均值 由 掩膜 区域计算得到 权重 由 局部方差 与用户输入参数计算确定 code // 局部均方差滤波 /*\brief 局部均方差滤波\param src:原矩阵&#xff0c;单通道\param res:结果矩阵\param size:掩膜矩形…

Linux学习之磁盘分区(fdisk、parted、df)、文件大小查看(ls、du)和文件写入(dd)

我用来演示的系统使用CentOS Linux release 7.9.2009 (Core)&#xff0c;内核版本是3.10.0-1160.71.1.el7.x86_64。 fdisk fdisk -l可以查看分区&#xff0c;因为我这里使用的不是root用户&#xff0c;所以前边加上sudo。 可以看到当前只有sda一块磁盘&#xff0c;第二磁盘…

全志V3S嵌入式驱动开发(软、硬件调试方法总结)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 v3s开发板本身是比较小巧的&#xff0c;ddr也是嵌入在soc里面的&#xff0c;电路设计也不复杂&#xff0c;因此很受电子爱好者的喜欢。不过大部分朋…

对话小红书剑寒:如何跨方向技术融合,全局优化打造更智能的音视频系统?...

编者按&#xff1a;在 AI 时代&#xff0c;随着 ChatGPT 掀起技术狂潮&#xff0c;构建更智能的音视频系统已经成为音视频领域从业者的共同目标。 然而&#xff0c;音视频系统的智能化&#xff0c;每个从业者对其都有自己独特的视角和理解&#xff0c;正如“一千个人眼中有一千…

华为官宣2024年推出面向商用的5.5G全套网络设备

2023年6月29日华为董事、ICT产品与解决方案总裁杨超斌在2023 MWC上海展5G Advanced论坛上宣布&#xff0c;2024年&#xff0c;华为将会推出面向商用的5.5G全套网络设备。这也标志着ICT行业即将迈入5.5G时代。 经过四年的高速发展&#xff0c;5G建设已经取得了丰硕的成果。目前…

【花雕】全国青少年机器人技术一级考试备考实操搭建手册7

随着科技的不断进步&#xff0c;机器人技术已经成为了一个重要的领域。在这个领域中&#xff0c;机械结构是机器人设计中至关重要的一部分&#xff0c;它决定了机器人的形态、运动方式和工作效率。对于青少年机器人爱好者来说&#xff0c;了解机械结构的基础知识&#xff0c;掌…

MySql的操作跟命令

概述 仅仅为了加强记忆&#xff0c;废话不说&#xff0c;直接上正文。 快速开始 创建数据库。 进入MySql客户端&#xff0c;输入root用户的密码 Enter password: **** Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 16 Server vers…

第3章 创建项目并初始化业务数据(过程记录)

项目声明和依赖 ECommerceRecommendSystem [pom.xml] 公用的声明、依赖、插件 properties 声明 log4g&#xff1a;处理日志的框架&#xff08;日志的具体实现&#xff09;sel4g&#xff1a;简单日志门面&#xff08;简单日志的接口&#xff09;mongodb-spark&#xff1a;Mong…

Python 元类实现ORM

目录 ORM概念 __new__、__init__、__call__的介绍 通过元类简单实现ORM中的insert功能 抽取到基类中 ORM概念 ORM&#xff08;Object Ralational Mapping&#xff0c;对象关系映射&#xff09;用来把对象模型表示的对象映射到基于 SQL 的关系模型数据库结构中去。这样&am…

隔断推拉门滑动不畅常见的原因和解决方法

隔断推拉门滑动不畅常见的原因和解决方法如下&#xff1a; 1. 滑轨污秽&#xff1a;如果滑轨上有灰尘、油垢或杂物积聚&#xff0c;会影响推拉门的滑动效果。解决方法是定期清洁滑轨&#xff0c;使用吸尘器或刷子清除污垢&#xff0c;并用湿布擦拭干净。 2. 滑轨损坏&#xff1…

Git---分支管理

文章目录 前言一、理解分支二、创建分支三、切换分支四、合并分支五、删除分支六、合并冲突七、分支管理策略八、分支策略九、bug分支十、删除临时分支总结 前言 本篇博客开始介绍Git的杀手级功能之一 : 分支.分支就是科幻电影里面的平行宇宙,当你正在电脑前努力学习C的时候,另…

MySQL学习基础篇(六)---多表查询

MySQL学习基础篇(六)—多表查询 对于多表查询的理解&#xff1a; 多表查询&#xff0c;也称为关联查询&#xff0c;指两个或更多个表一起完成查询操作。 前提条件&#xff1a;这些一起查询的表之间是有关系的&#xff08;一对一、一对多&#xff09;&#xff0c;它们之间一定是…

浅析EasyCVR视频技术与AR实景智能管理平台在智慧厂区中的应用

一、背景分析 新型智慧厂区是运用人工智能、大数据、物联网和设备监控技术加强厂区安保和信息管理。通过先进技术&#xff0c;保障厂区生产运营安全&#xff0c;同时减少生产线上的人工干预、及时正确地采集各类生产数据&#xff0c;以及合理的生产计划编排与生产进度&#xff…

线性规划的对偶问题(The Dual of LP)

线性规划的对偶问题&#xff08;The Dual of LP&#xff09; 对偶理论是线性规划中最重要的理论之一&#xff0c;是深入了 解线性规划问题结构的重要理论基础。同时&#xff0c;由于问题提 出本身所具有的经济意义&#xff0c;使得它成为对线性规划问题系 统进行经济分析和敏感…