SQL执行流程图文分析:从连接到执行的全貌

SQL执行总流程

下面就是 MySQL 执行一条 SQL 查询语句的流程,也从图中可以看到 MySQL 内部架构里的各个功能模块。

MySQL 的架构共分为两层:Server 层和存储引擎层

  • Server 层负责建立连接、分析和执行 SQL。MySQL 大多数的核心功能模块都在这实现,主要包括连接器,查询缓存、解析器、预处理器、优化器、执行器等。另外,所有的内置函数(如日期、时间、数学和加密函数等)和所有跨存储引擎的功能(如存储过程、触发器、视图等。)都在 Server 层实现。

  • 存储引擎层负责数据的存储和提取。支持 InnoDB、MyISAM、Memory 等多个存储引擎,不同的存储引擎共用一个 Server 层。现在最常用的存储引擎是 InnoDB,我们常说的索引数据结构,就是由存储引擎层实现的,不同的存储引擎支持的索引类型也不相同,比如 InnoDB 支持索引类型是 B+树 ,且是默认使用,也就是说在数据表中创建的主键索引和二级索引默认使用的是 B+ 树索引。

接下来,就详细说一条 SQL 查询语句的执行流程,依次看看每一个功能模块的作用。

第一步:连接器

在 Linux 操作系统里要使用 MySQL,那你第一步肯定是要先连接 MySQL 服务,然后才能执行 SQL 语句

  1. 连接的过程需要先经过 TCP 三次握手,因为 MySQL 是基于 TCP 协议进行传输的。

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

  2. 如果用户名和密码都对了,会读取该用户的权限,然后后面的权限逻辑判断都基于此时读取到的权限

注意:MySQL 的连接数有限制

MySQL 服务支持的最大连接数由 max_connections 参数控制,比如我的 MySQL 服务默认是 151 个,超过这个值,系统就会拒绝接下来的连接请求,并报错提示“Too many connections”。

短连接和长连接

MySQL 的连接也跟 HTTP 一样,有短连接和长连接的概念,它们的区别如下:

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

使用长连接的好处就是可以减少建立连接和断开连接的过程,所以一般是推荐使用长连接

但是!长连接虽然方便,但用多了可能会让数据库占用太多内存,严重时可能导致数据库服务意外重启。

        想象一下,你的电脑是一个图书馆,而数据库查询就像是借书。每次你借书时,图书馆都需要记录你的借阅信息。如果你借了很多书而且不还,图书馆就需要用更多的空间来记录这些信息。同样的,当你保持很多长连接时,MySQL服务器就需要用更多的内存来管理这些连接。

        如果长连接太多,MySQL服务器就需要用很多内存来“记住”这些连接。如果内存用得太多,系统可能会因为资源不足而强制关闭一些程序。MySQL就可能会突然重启

解决长连接占用内存问题

  • 第一种,定期断开长连接。既然断开连接后就会释放连接占用的内存资源,那么我们可以定期断开长连接。

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

第二步:查询缓存

        连接器得工作完成后,客户端就可以向 MySQL 服务发送 SQL 语句了,MySQL 服务收到 SQL 语句后,就会解析出 SQL 语句的第一个字段,看看是什么类型的语句。

  • 如果是事务型(增删改)语句,则跳过这一步

  • 如果查询语句,MySQL 就会先去查询缓存里查找缓存数据,这个查询缓存是以 key-value 形式保存在内存中的,key 为 SQL 查询语句,value 为 SQL 语句查询的结果。

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

        但是其实查询缓存挺鸡肋的,对于更新比较频繁的表,查询缓存的命中率很低的,因为只要一个表有更新操作,那么这个表的查询缓存就会被清空。所以,MySQL 8.0 版本直接将查询缓存删掉了,也就是说 MySQL 8.0 开始,执行一条 SQL 查询语句,不会再走到查询缓存这个阶段了。

注意:

        这里说的查询缓存是 server 层的,也就是 MySQL 8.0 版本移除的是 server 层的查询缓存,并不是 Innodb 存储引擎中的 buffer pool。

第三步:解析 SQL

在正式执行 SQL 查询语句之前, MySQL 会先对 SQL 语句做解析,这个工作交由「解析器」来完成。

解析器会做如下两件事情。

  • 第一件事情,词法分析。MySQL 会根据你输入的字符串识别出关键字出来,例如,SQL语句 select username from userinfo,在分析之后,会得到4个Token,其中有2个Keyword,分别为select和from

  • 第二件事情,语法分析。根据词法分析的结果,语法解析器会根据语法规则,判断你输入的这个 SQL 语句是否满足 MySQL 语法,如果没问题就会构建出 SQL 语法树,这样方便后面模块获取 SQL 类型、表名、字段名、 where 条件等等。

        如果我们输入的 SQL 语句语法不对,就会在解析器这个阶段报错。但是注意,解析器只负责检查语法和构建语法树,但是不会去查表或者字段存不存在。

第四步:执行 SQL

经过解析器后,接着就要进入执行 SQL 查询语句的流程了,每条SELECT 查询语句流程主要可以分为下面这三个阶段:

  • prepare 阶段,也就是预处理阶段;

  • optimize 阶段,也就是优化阶段;

  • execute 阶段,也就是执行阶段;

预处理器

预处理阶段做了什么事情。

  • 检查 SQL 查询语句中的表或者字段是否存在;

  • select * 中的 * 符号,扩展为表上的所有列;

        不过,对于 MySQL 5.7 判断表或字段是否存在的工作,是在词法分析&语法分析之后,prepare 阶段之前做的。结论都一样,不是在解析器里做的。MySQL 8.0 代码结构变化很大,后来判断表或字段是否存在的工作就被放入到 prepare 阶段做了。

优化器

经过预处理阶段后,还需要为 SQL 查询语句先制定一个执行计划,这个工作交由「优化器」来完成的。

        优化器主要负责将 SQL 查询语句的执行方案确定下来,比如在表里面有多个索引的时候,优化器会基于查询成本的考虑,来决定选择使用哪个索引。

        要想知道优化器选择了哪个索引,我们可以在查询语句最前面加个 explain 命令,这样就会输出这条 SQL 语句的执行计划,然后执行计划中的 key 就表示执行过程中使用了哪个索引

执行器

        经历完优化器后,就确定了执行方案,接下来 MySQL 就真正开始执行语句了,这个工作是由「执行器」完成的。在执行的过程中,执行器就会和存储引擎交互了,交互是以记录为单位的。

接下来,跟大家说一下执行器和存储引擎的交互过程

  • 全表 | 索引扫描

  • 索引下推

全表 | 主键索引查询
  • 当查询是基于主键进行时,执行器可以直接使用主键索引来快速定位到特定的行记录。如果记录是存在的,就会将记录返回给执行器

  • 如果是全表的,其实也差不多,在全表中依次拉取一堆数据来返回给执行器,然后再拉取下一堆返回,一次性加载所有记录可能会消耗大量的内存资源,尤其是在处理大型数据集时。分批次处理可以避免内存过载,提高系统的稳定性和性能。

        执行器从存储引擎读到记录后,它会接着检查这些记录是否还满足查询中的其他过滤条件,对于每个条件,执行器都会检查记录是否满足,不满足就丢弃,这个过程发生在Server层,Server 层每从存储引擎读到一条记录就会发送给客户端,之所以客户端显示的时候是直接显示所有记录的,是因为客户端是等查询语句查询完成后,才会显示出所有的记录

索引下推

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

举一个具体的例子,方便大家理解,这里一张用户表如下,我对 age 和 reward 字段建立了联合索引(age,reward):

现在有下面这条查询语句:

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

联合索引当遇到范围查询 (>、<) 就会停止匹配,也就是 age 字段能用到联合索引,但是 reward 字段则无法利用到索引

那么,不使用索引下推(MySQL 5.6 之前的版本)时,执行器与存储引擎的执行流程是这样的:

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

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

  • Server 层在判断该记录的 reward 是否等于 100000,如果成立则将其发送给客户端;否则跳过该记录;

  • 接着,继续向存储引擎索要下一条记录,存储引擎在二级索引定位到记录后,获取主键值,然后回表操作,将完整的记录返回给 Server 层;

  • 如此往复,直到存储引擎把表中的所有记录读完。

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

而使用索引下推后,判断记录的 reward 是否等于 100000 的工作交给了存储引擎层,过程如下 :

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

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

  • Server 层在判断其他的查询条件(本次查询没有其他条件)是否成立,如果成立则将其发送给客户端;否则跳过该记录,然后向存储引擎索要下一条记录。

  • 如此往复,直到存储引擎把表中的所有记录读完。

可以看到,使用了索引下推后,虽然 reward 列无法使用到联合索引,但是因为它包含在联合索引(age,reward)里,所以直接在存储引擎过滤出满足 reward = 100000 的记录后,才去执行回表操作获取整个记录。相比于没有使用索引下推,节省了很多回表操作。

当你发现执行计划里的 Extr 部分显示了 “Using index condition”,说明使用了索引下推。

总结

执行一条 SQL 查询语句,期间发生了什么?

  • 连接器:建立连接,管理连接、校验用户身份;
  • 查询缓存:查询语句如果命中查询缓存则直接返回,否则继续往下执行。MySQL 8.0 已删除该模块;
  • 解析 SQL,通过解析器对 SQL 查询语句进行词法分析、语法分析,然后构建语法树,方便后续模块读取表名、字段、语句类型;
  • 执行 SQL:执行 SQL 共有三个阶段:
    • 预处理阶段:检查表或字段是否存在;将 select * 中的 * 符号扩展为表上的所有列。
    • 优化阶段:基于查询成本的考虑, 选择查询成本最小的执行计划;
    • 执行阶段:根据执行计划执行 SQL 查询语句,从存储引擎读取记录,返回给客户端;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

员工管理系统!(免费获取源码)

​今天给大家分享一套基于SpringbootVue的员工管理系统源码&#xff0c;在实际项目中可以直接复用。(免费提供&#xff0c;文末自取) 一、系统运行图 1、登陆页面 2、后台管理页面 3、职工管理 4、请假审批管理 二、系统搭建视频教程 源码免费领取方式 后台私信回复员工即可…

从大量数据到大数据,King’s SDMS仪器数据采集及科学数据管理系统的应用

对于实验室或检测机构&#xff0c;仪器设备是所有业务开展的基础&#xff0c;数据则是核心命脉&#xff0c;而传统的仪器设备原始数据收集方式&#xff0c;效率低耗时长、操作流程不规范、不易保存与查找、错误率高、易篡改等成了制约检测机构持续高速发展的瓶颈和弊端&#xf…

kvm虚拟机磁盘镜像加密

一、qcow2的aes加密 低版本的qemu能够支持对qcow2文件进行aes加密的方式&#xff0c;例如对一个已经存在的磁盘文件test.qcow2&#xff0c;可以将其转换为经过加密的qcow2文件。 qemu-img convert -O qcow2 --object secret,idsec0,data123456 -o encryptionon,encrypt.key-s…

springboot发送邮件

很久之前就想写一个总结的&#xff0c;一直没写&#xff0c;今天刚好又碰见了发送邮箱验证码的需求&#xff0c;刚好记录一波 一.核心依赖如下&#xff1a; <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-par…

Python 全栈体系【四阶】(二十九)

第五章 深度学习 四、TensorFlow 5. 张量及基本运算 5.1 张量的阶与形状 阶&#xff1a;张量的维度&#xff08;数方括号的层数&#xff09; 形状表示方法 0 维&#xff1a;( )1 维&#xff1a;(5)&#xff0c;1 行 5 个元素2 维&#xff1a;(2,3)&#xff0c;2 行 3 列3…

redis数据类型介绍

Redis是一种开源的高性能内存数据存储系统&#xff0c;支持多种数据结构的操作。下面是Redis支持的五种数据类型介绍&#xff1a; 1. 字符串&#xff08;string&#xff09;&#xff1a;Redis中字符串是二进制安全的&#xff0c;允许存储任何格式的数据&#xff0c;如图片、视频…

Springboot集成RabbitMq+延时队列

1. 引入jar包 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> 2.配置yml 2.1 配置生产者yml spring:rabbitmq:host: localhostport: 5672virtual-host: …

UE 模型学习

1. UE中任何模型都是有多个三角面构成的&#xff0c;模型有一个顶点数组&#xff0c;根据右手螺旋定则和三个顶点顺序确定三角面的法线。 注&#xff1a;当三角面的法线方向与相机法线方向相同时&#xff0c;此时看不见三角面&#xff1b;当两法线方向相反&#xff0c;此时才能…

数据结构DAY5--二叉树相关流程

流程有&#xff1a;创建->遍历->得到信息->销毁 创建 根据先序遍历的流程以及对叶子结点的左后驱结点和右后驱结点以#号替代的原则&#xff0c;写出一个数组&#xff0c;并建立一个结构体&#xff0c;包括数据域&#xff0c;结构体类型的左后驱结点和右后驱结点指针…

数字证书在网络安全中的关键作用与日常应用

在当今数字化的时代&#xff0c;网络安全问题日益凸显&#xff0c;保护数据安全和用户隐私成为了人们关注的焦点。数字证书作为一种重要的网络安全技术&#xff0c;其在网络安全中扮演着关键的角色&#xff0c;并且在我们的日常生活中有着广泛的应用。现在给大家介绍简单介绍下…

blender怎么用GPU渲染?blender GPU云渲染推荐

在三维建模和渲染领域&#xff0c;Blender以其强大的功能和免费开源的特点广受好评。GPU渲染作为提升渲染效率的关键技术&#xff0c;越来越受到用户的关注。本文将详细介绍如何在Blender中设置并利用GPU进行渲染&#xff0c;以及探索其云渲染的可能性&#xff0c;助力用户高效…

装机指导。

everything winrar snipaste cmake git tortoisegit tortoisesvn inno setup vs2022 安装的时候注意sdk路径一定要默认&#xff01;&#xff01; 否则你会发现在你的sdk安装路径的根盘符下会多出一个Windows Kits&#xff0c;强迫症接受不了 默认的会跟已有的装在一起…

无法用raven-js,如何直接使用TraceKit标准化错误字符串(一次有趣的探索)

引子&#xff1a;网上三年前&#xff08;2020&#xff09;的文章介绍了一个raven-js 简单说就是把堆栈信息格式化兼容各浏览器&#xff0c;便于查看错误来源。 **but&#xff1a;**到处找了一下raven-js&#xff0c;已经没有官方出处了&#xff0c;只在Sentry的源码仓库里发现…

林江院长赴长沙见证爱尔眼科巩膜镜技术诊疗门诊启动仪式

近日&#xff0c;爱尔眼科“巩膜镜技术诊疗门诊、视觉康复及训练门诊”启动会在湖南长沙顺利举行。旨在通过成立爱尔眼科巩膜镜技术诊疗门诊、视觉康复及训练门诊&#xff0c;为有需要的疑难屈光不正患者提供全新的诊疗途径&#xff0c;为各年龄阶段人群视觉问题提供更全面的个…

[数据结构初阶]二叉树

我们在前两篇博客中主要介绍了堆及其应用&#xff0c;针对的对象堆是完全二叉树&#xff0c;存储方式采用顺序结构存储的方式。 那么好的&#xff0c;这篇博客我们浅谈二叉树的链式存储&#xff0c;针对的对象是二叉树&#xff0c;并不局限于完全二叉树了&#xff01; 我们先来…

PlayerSettings.WebGL.emscriptenArgs设置无效的问题

1&#xff09;PlayerSettings.WebGL.emscriptenArgs设置无效的问题 2&#xff09;多个小资源包合并为大资源包的疑问 3&#xff09;AssetBundle在移动设备上丢失 4&#xff09;Unity云渲染插件RenderStreaming&#xff0c;如何实现多用户分别有独立的操作 这是第381篇UWA技术知…

MySOL之旅--------MySQL数据库基础( 3 )

本篇碎碎念:要相信啊,胜利就在前方,要是因为一点小事就停滞不前,可能你也不适合获取胜利,成功的路上会伴有泥石,但是走到最后,你会发现身上的泥泞皆是荣耀的勋章! 今日份励志文案: 凡是发生皆有利于我 目录 查询(select) 1.全列查询 2.指定列查询 3.查询字段为表达式 ​编…

PVE系统的安装

一.PVE系统的安装 前置准备环境:windows电脑已安装Oracle VM VirtualBox,电脑支持虚拟化,且已经开启,按住ctrl+shift+ESC打开任务管理器查看是否开启,如果被禁用,可进入BIOS开启虚拟化,重启电脑后再进行后续操作。本步骤选用windows10安装VirtualBox,版本为7.0.8。 …

被拒绝的职场空窗期,到底该怎么办?

打工人的心头刺 最近&#xff0c;一则新闻在网上炸开了锅&#xff1a;一位求职者因职场空窗期超过三个月&#xff0c;竟被无情拒绝应聘。消息一出&#xff0c;瞬间引起了广大职场人的共鸣。在这个快节奏的时代&#xff0c;我们似乎被一种无形的力量推着&#xff0c;不敢休息&am…

高性能代码如何编写?

引言&#xff1a; 性能优化一直是一个至关重要的议题。随着应用程序规模的不断增长和用户对性能的不断提升的要求&#xff0c;开发人员需要更加关注如何编写高性能的代码&#xff0c;以确保应用程序能够在各种情况下都能保持稳定和高效。编写高性能代码需要从多个方面入手&…