查询语句执行流程

查询语句的执行流程如下:权限校验、查询缓存、分析器、优化器、权限校验、执行器、引擎。

举个例子,查询语句如下:

select * from user where id > 1 and name = 'seven';
  1. 首先,通过连接器,客户端与MySQL服务器建立连接,并完成身份认证和权限验证过程。在此过程中,客户端需要提供用户名和密码以证明其合法性,服务器则会对这些信息进行核对。
  2. 检查是否开启缓存。MySQL 8.0之前,Query Cache 确实会缓存完全相同的查询结果,以便重复执行相同查询时直接返回缓存数据。然而,MySQL 8.0及以后版本已经完全弃用Query Cache,因此在MySQL 8.0及更高版本中这一步骤不在适用。
  3. MySQL的解析器会对查询语句进行解析,检查语法是否正确,并将查询语句转换为内部数据结构。预处理器则会根据MySQL的规则进一步检查解析树是否合法,如检查数据表或数据列是否存在等。
  4. 优化器会根据查询语句的结构、表的统计信息等因素,生成多个可能的执行计划,并通过成本估算器选出最优的执行计划。两种执行方案,先查 id > 1 还是 name = 'seven',优化器根据自己的优化算法选择执行效率最好的方案;这一步旨在提高查询效率,降低资源消耗。
  5. 执行器按照优化器选择的执行计划,调用存储引擎的API来执行查询。存储引擎负责实际的数据存储和检索,根据执行器的请求,读取或写入数据。
  6. 存储引擎负责实际的数据存储和检索工作,根据执行器的请求,读取或写入数据。
  7. 如果开启了Query Cache且查询结果能够命中缓存,查询结果会从缓存中直接返回。而如果没有开启Query Cache或缓存没有命中,MySQL会直接返回查询结果。

更新语句执行过程

更新语句执行流程如下:分析器、权限校验、执行器、引擎、redo log(prepare状态)、binlog、redo log(commit状态)

举个例子,更新语句如下:

update user set name = 'seven' where id = 1;

具体的执行流程如下图:

  1. 找存储引擎取到 id = 1 这一行记录。
  2. 根据主键索引树找到这一行,如果 id = 1 这一行所在的数据页本来就在内存池(Buffer Pool)中,就直接返回给执行器;否则,需要先从磁盘读入内存池,然后再返回。
  3. 记录Undo Log日志,对数据进行备份,便于回滚。
  4. 拿到存储引擎返回的行记录,把 name 字段设置为 “seven”,得到一行新的记录,然后再调用存储引擎的接口写入这行新记录。
  5. 将这行新数据更新到内存中,同时将这个更新操作记录到 Redo Log 里面,为 Redo Log 中的事务打上 prepare 标识。然后告知执行器执行完成了,随时可以提交事务。
  6. 生成这个操作的 Binlog,并把 Binlog 写入磁盘。
  7. 提交事务。
  8. 把刚刚写入的 Redo Log 状态改成提交(commit)状态,更新完成。

关于以上日志的介绍,可以看这篇文章

以上只是一个简单的case,方便我们能够简单的熟悉流程。接下来,我们对update过程中的全流程进行梳理,具体的流程如下图:

  1. 首先客户端发送一条 SQL 语句到 Server 层的 SQL interface。
  2. SQL interface 接到该请求后,先对该条语句进行解析,验证权限是否匹配,也就是在我们上文中讲到的执行器中在执行。
  3. 验证通过以后,分析器会对该语句分析,是否语法有错误等。
  4. 接下来是优化器生成相应的执行计划,选择最优的执行计划,然后是执行器根据执行计划执行这条语句。
  5. 执行器从Buffer Pool中获取数据页的数据,如果数据页没有,需要从磁盘中进行加载。
  6. 开启事务,修改数据之前先记录Undo Log,写入Buffer Pool的Undo Page。
  7. 开始更新数据页中的记录,被修改的数据页称为脏页,修改会被记录到内存中的 Redo Log Buffer中,再刷盘到磁盘的Redo Log文件,此时事务是 perpare阶段。
  8. 这个时候更新就完成了,当时脏页不会立即写入磁盘,而是由后台线程完成,这里会用double write来保证脏页刷盘的可靠性。
  9. 通知Server层,可以正式提交数据了, 执行器记录Binlog cache,事务提交时才会将该事务中的Binlog刷新到磁盘中。
  10. 这个时候Update语句完成了Buffer Pool中数据页的修改、Undo Log、Redo Log缓存记录,以及记录Binlog cache缓存。
  11. commit阶段,这个阶段是将Redo Log中事务状态标记为commit。
  12. 此时Binlog和Redo Log都已经写入磁盘,如果触发了刷新脏页的操作,先把脏页copy到double write buffer里,double write buffer 的内存数据刷到磁盘中的共享表空间 ibdata,再刷到数据磁盘上数据文件 ibd。

以上就是修改语句的全部流程,为什么记录完redo log,不直接提交,而是先进入prepare状态?

这里涉及到两阶段提交问题。假设先写redo log直接提交,然后写binlog,写完redo log后,机器挂了,binlog日志没有被写入,那么机器重启后,这台机器会通过redo log恢复数据,但是这个时候binlog并没有记录该数据,后续进行机器备份的时候,就会丢失这一条数据,同时主从同步也会丢失这一条数据。

Mysql从连接到执行的全过程详解

执行流程图

MySQL 的架构可以大致划分为四个层次:连接层、服务层、存储引擎层和文件系统层。

  • 连接层:负责对来自客户端的连接进行权限验证,并将连接信息存入连接池中,方便后续的连接复用。
  • 服务层:主要负责 SQL 语句的解析与优化,还包括查询缓存和 MySQL 内置函数的实现。
  • 存储引擎层:提供多种可插拔的存储引擎,允许我们通过不同的引擎进行数据的存取操作。存储引擎使得 MySQL 能够直接与硬盘上的数据和日志进行交互,用户可以根据需求选择合适的引擎。从 MySQL 5.5 版本开始, InnoDB 成为了 MySQL 的默认存储引擎。
  • 文件系统层:这一层主要包括日志文件、数据文件及与 MySQL 相关的其他程序。在这四个层次中,服务层和存储引擎层构成了架构的核心。服务层负责处理 MySQL 的核心逻辑,而存储引擎层则直接负责数据的存取操作。

也可以将其简单的分成两层:Server 层和存储引擎层,如图

  • Server 层:负责建立连接、分析和执行SQL。主要包括连接器、查询缓存、分析器、优化器、执行器等。这些组件包含了MySQL的大部分主要功能。
  • 存储引擎层:负责数据的存储和提取

连接器

客户端需要通过连接器访问MySQL Server,连接器主要负责身份认证和权限鉴别的工作。也就是负责用户登录数据库的相关认证操作,例如:校验账户密码,权限等。在用户名密码合法的前提下,会在权限表中查询用户对应的权限,并且将该权限分配给用户。

如何查看有多少连接?

执行 show processlist 命令进行查看



其中”Command”列返回的内容中,“Sleep”表示MySQL相同中对应一个空闲连接。而“Query”表示正在查询的连接。

连接状态:

Command 含义
sleep 线程正在等待客户端发数据
query 连接线程正在执行查询
locked 线程正在等待表锁的释放
sorting result 线程正在对结果进行排序
sending data 向请求端返回数据

空闲连接是否一直存在

从上图可以看出有许多空闲连接,MySQL 定义了空闲连接的最大空闲时长,由 wait_timeout 参数控制的,默认值是 8 小时(28880秒),如果空闲连接超过了这个时间,连接器就会自动将它断开。

手动断开空闲的连接,使用的是 kill connection + id 的命令

最大连接数

长连接和短连接

  • 长连接是指连接成功后,客户端请求一直使用是同一个连接。
  • 短连接是指每次执行完SQL请求的操作之后会断开连接,如果再有SQL请求会重新建立连接。

由于短连接会反复创建连接消耗相同资源,因此多数情况下会选择长连接。但是为了保持长连接,会占用系统内存,而这些被占用的内存知道连接断开以后才会释放。这里提出了两个解决方案:

  1. 定期断开长连接,每隔一段时间或者执行一个占用内存的大查询以后断开连接,从而释放内存,当查询的时候再重新创建连接。
  2. 客户端主动重置连接。MySQL 5.7 或者更高的版本,通过执行 mysql_reset_connection 来重新初始化连接。此过程不会重新建立连接,但是会释放占用的内存,将连接恢复到刚刚创立连接的状态。

查询缓存

在建立与数据库的连接以后就可以执行SQL语句了

如果 SQL 是查询语句(select 语句),MySQL 就会先去查询缓存( Query Cache )里查找缓存数据,看看之前有没有执行过这一条命令,并且将执行结果按照key-value的形式缓存在内存中了。

Key 是查询的SQL语句,Value 是查询的结果。如果缓存 Key 被命中,就会直接返回给客户端,如果没有命中,就会执行后续的操作,执行完SQL仍旧会把结果缓存起来,方便下一次调用。

Mysql的机制是只要一个表有更新操作,那么这个表的查询缓存就会被清空。如果张表不断地被使用(更新、查询),那么查询缓存会频繁地失效,获取查询缓存也失去了意义。不过可以运用在一些修改不频繁的数据表。例如:系统配置、或者修改不频繁的表。

缓存的淘汰策略是先进先出,适用于查询远大于修改的情况下, 否则建议使用Redis或者其他做缓存工具。因此大多数情况下不推荐使用查询缓存。MySQL 8.0 版本后删除了查询缓存的功能,官方认为该功能应用场景较少,所以将其删除。

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

解析 SQL

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

解析器会做两件事情

  1. 词法分析。MySQL 会根据你输入的字符串识别出关键字出来,构建出 SQL 语法树,这样方便后面模块获取 SQL 类型、表名、字段名、 where 条件等等。
  2. 语法分析。根据词法分析的结果,语法解析器会根据语法规则,判断你输入的这个 SQL 语句是否满足 MySQL 语法。
  3. 语义分析:语义分析主要是检查 SQL 语句中的每个对象是否符合数据库的实际情况。如表名、字段名是否存在,用户是否对相关表和列拥有执行权限,数据类型是否匹配等。

如果输入的 SQL 语句语法不对,就会在解析器这个阶段报错。比如把 from 写成了 form,这时 MySQL 解析器就会给报错.

执行SQL语句

每条SELECT 查询语句流程可以分为三个阶段:

  1. prepare 阶段,也就是预处理阶段;
  2. optimize 阶段,也就是优化阶段;
  3. execute 阶段,也就是执行阶段;

预处理器

预处理器的作用:

  1. 检查 SQL 查询语句中的表或者字段是否存在;
  2. 将 select * 中的 * 符号,扩展为表上的所有列;

优化器

优化器的主要任务是对 SQL 查询进行优化,生成一个最优的执行计划,从而提高查询性能。优化器的工作基于查询的解析树和元数据,它会尝试在不同的查询执行策略中选择效率最高的一个。

在查询优化器中,分为逻辑查询优化和物理查询优化两个大块

  • 逻辑优化会进行一些逻辑层面的优化,主要目的是通过调整 SQL 语句的结构来提高查询效率。包括:

    • 消除冗余的子查询:将某些子查询转换为连接或合并查询。
    • 重写查询:比如将 OR 条件转换为 UNION 操作。
    • 查询合并:将多个查询合并成一个查询。
    • 移除不必要的操作:例如消除不需要的 ORDER BY 或 DISTINCT。
  • 物理查询优化是根据数据库的具体执行引擎、索引、统计信息等做出的决策。这个阶段会根据优化器评估的成本模型选择合适的执行计划。具体的优化措施包括:

    • 选择合适的连接方式:比如选择 Nested Loop Join、Hash Join 或 Sort Merge Join。
    • 选择索引:通过选择合适的索引来加速数据访问。
    • 选择合适的排序方式:通过使用索引扫描或临时表来避免全表扫描。

优化器会使用基于成本的模型(Cost-Based Optimization)来评估每种查询执行计划的成本,选择成本最低的执行计划。其核心是通过计算不同执行计划的资源消耗(如 CPU 时间、I/O 操作等),并选出最优的执行策略。

优化器的目标是通过多种优化策略来降低查询的执行成本,生成一个尽可能高效的执行计划。它在逻辑层面和物理层面对 SQL 查询进行优化,以减少查询执行所需的资源。

执行器

当解析器生成查询计划,并且经过优化器以后,就到了执行器。

在执行之前,执行器会首先检查用户是否有权限执行相应的操作。如果没有权限,则返回错误信息。

执行器的主要工作包括:

  • 表扫描:根据查询条件决定是否使用索引、是否全表扫描。
  • 连接操作:根据优化器选择的连接方式(如嵌套循环连接、哈希连接等)执行表之间的数据合并。
  • 排序和聚合:执行查询中的 ORDER BY、GROUP BY 等操作。
  • 数据返回:查询结果被返回给用户,修改操作则会提交事务。

对于涉及数据修改的 SQL(如 INSERT、UPDATE、DELETE 等),执行器还需要管理事务的提交和回滚操作,确保数据的一致性和持久性。这些操作会与 MySQL 的日志系统(Undo Log、Redo Log、Binlog) 密切交互,确保事务的 ACID 属性。

执行器根据优化器生成的执行计划实际执行 SQL 查询,完成数据操作,返回查询结果或更新数据库状态。它是查询执行的最后环节,直接与 MySQL 的存储引擎进行交互。

总结

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

Mysql查询语句执行流程?更新语句执行流程?的更多相关文章

  1. mysql查询当天所有数据sql语句

    mysql查询当天的所有信息: select * from test where year(regdate)=year(now()) and month(regdate)=month(now()) a ...

  2. 【转】mysql查询当天所有数据sql语句

    mysql查询当天的所有信息: select * from test where year(regdate)=year(now()) and month(regdate)=month(now()) a ...

  3. Oracle数据库——Mybatis在一个update标签下执行多更新语句

    begin update table table1 set com1 ='1' ; update table table2 set com1 ='1' ; end;

  4. mysql查询 if判断、case语句的使用等

    一. 查询的数字转换为中文返回前端 1. 如果是0或1表状态等,可用: IF(字段 = 0, '否', '是') AS xxx 2. 如果是多个值,比如1,2,3可用: ELT(字段, '计划治理', ...

  5. mysql INSERT ... ON DUPLICATE KEY UPDATE语句

    网上关于INSERT ... ON DUPLICATE KEY UPDATE大多数文章都是同一篇文章转来转去,首先这个语法的目的是为了解决重复性,当数据库中存在某个记录时,执行这条语句会更新它,而不存 ...

  6. MySQL查询缓存详解(总结)

    MySQL查询缓存详解(总结) 一.总结 一句话总结: mysql查询缓存还是可以用用试一试,但是更推荐分布式,比如redis/memcache之流,将数据库中查询的数据和查询语句以键值对的方式存进分 ...

  7. MySQL数据库详解(一)执行SQL查询语句时,其底层到底经历了什么?

    一条SQL查询语句是如何执行的? 前言 ​ 大家好,我是WZY,今天我们学习下MySQL的基础框架,看一件事千万不要直接陷入细节里,你应该先鸟瞰其全貌,这样能够帮助你从高维度理解问题.同样,对于MyS ...

  8. mysql实战45讲读书笔记(二) 一条SQL更新语句是如何执行的 极客时间

    前面我们系统了解了一个查询语句的执行流程,并介绍了执行过程中涉及的处理模块.相信你还记得,一条查询语句的执行过程一般是经过连接器.分析器.优化器.执行器等功能模块,最后到达存储引擎. 那么,一条更新语 ...

  9. 《Mysql 一条 SQL 更新语句是如何执行的?(Redo log)》

    一:更新流程 - 对于更新来说,也同样会根据 SQL 的执行流程进行. -  - 连接器 - 连接数据库,具体的不做赘述. - 查询缓存 - 在一个表上有更新的时候,跟这个表有关的查询缓存会失效. - ...

  10. MySQL中一条更新语句是如何执行的

    1.创建表的语句和更新的语句 这个表的创建语句,这个表有一个主键ID和一个整型字段c: mysql> create table T(ID int primary key, c int); 如果要 ...

随机推荐

  1. 【工具】FFmpeg|压缩视频,500MB变25MB(有损,支持 Windows、Linux、macOS 各个平台)

    参考: 如何将一分钟长的1080p视频压缩至5MB以内?-知乎-滔滔清风 近期HEVC扩展备用安装方法-B站-悲剧天下 总共三个步骤,安装FFmpeg.运行指令.打开视频. 亲测 500MB 变 25 ...

  2. Swiper.js滑动插件使用教程

    几乎每个前端开发都应该用过这个滑动组件库吧?这就是大名鼎鼎的swiper.js 一.Swiper及其功能 Swiper.js 是一个流行的开源的移动端触摸滑动库,用于创建响应式.可触摸滑动的轮播图.滑 ...

  3. MyCAT 2实现mysql8主从同步读写分离

    一.MyCAT2介绍 1.1 什么是MyCAT?MyCAT是目前最流行的分布式数据库中间插件,是一个开源的分布式数据库系统,是一个实现了MySQL协议的服务器.前端用户可以把它看作一个数据库代理,用M ...

  4. Google Cloud Next大会上的耀眼新星:探索最具潜力的AI初创公司

    在拉斯维加斯举办的Google Cloud Next大会上,不仅揭晓了如Ironwood处理器和Gemini 2.5 Flash等重磅新技术,还展示了一系列使用谷歌云计算服务的最有趣的初创公司.这些创 ...

  5. 【.NET必读】RabbitMQ 4.0+重大变更!C#开发者必须掌握的6大升级要点

    RabbitMQ 作为一款广受欢迎的消息队列中间件,近年来从 3.x 版本升级到 4.0+,带来了显著的功能增强和架构调整.与此同时,其官方 C# 客户端也从 6.x 版本跃升至 7.0,引入了全新的 ...

  6. 代码随想录第十天 | 栈与队列part02

      第五章 栈与队列part02 150. 逆波兰表达式求值 本题不难,但第一次做的话,会很难想到,所以先看视频,了解思路再去做题 题目链接/文章讲解/视频讲解:https://programmerc ...

  7. Windows加域后通过注册表取消壁纸锁定更换壁纸

    打开注册表: 开始--运行--regedit (快捷键Win+R) 然后找到HKEY_CURRENT_USER ==> Software ==> Microsoft ==> Wind ...

  8. C# 锁机制全景与高效实践:从 Monitor 到 .NET 9 全新 Lock

    引言:线程安全与锁的基本概念 线程安全 在多线程编程中,保障共享资源的安全访问依赖于有效的线程同步机制.理解并处理好以下两个核心概念至关重要: 线程安全:指某个类.方法或数据结构能够在被多个线程同时访 ...

  9. ChatMoney,你的就业指导明灯

    本文由 ChatMoney团队出品 介绍说明 Hey!亲爱的小伙伴们,今天我要给大家带来一个职场利器--AI就业指导机器人! 在这个充满变数的职场江湖,找到一份既能养家糊口又能实现自我价值的工作是多么 ...

  10. C# Json 解析,针对数字开头变量Json字符串转模型(Model)

    namespace 解析数字开头的变量JSON { class Program { static void Main(string[] args) { string strJson = "{ ...