从头开始搞懂mysql(01)如何执行一条sql查询语句(代码片段)

一起来搬砖呀 一起来搬砖呀     2022-12-10     360

关键词:

1、如何执行一条 SQL 查询语句

做一件事情我们最好先鸟瞰全貌,从高纬度理解问题。对于 MySQL 的学习我们也是这样。
平时我们使用数据库,看到的一般都是一个整体。

比如:一个最简单的表 test,表中只有一个字段 ID,我们执行下面的查询语句

SELECT * FROM test WHERE ID = 1;

我们看到的仅仅只有输入一条语句,返回一个结果,却不了解这条语句在 MySQL 内的执行过程

所以今天让我们一起把 MySQL 拆开,看看里面都有哪些零件,通过这个拆解让我们对 MySQL 有更深入的理解,让我们在碰到 MySQL 的一些异常或者问题的时候,能够快速定位并解决问题

1.1 MySQL基础架构

下面是 MySQL 的基本架构示意图,可以清楚地看到 SQL 语句在 MySQL 的各个功能模块中的执行顺序

总体来说,MySQL 可以分为 Server 层和存储引擎两部分

  • Server 层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖了 MySQL 的大多数核心功能服务,以及所有的内置函数(日期、时间、数学、加密等),所有的跨库存引擎都在该层实现,比如存储过程、触发器、视图等

  • 存储引擎负责数据的存储和提取,架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。现在常用的是 InnoDB,MySQL 5.5.5 版本就开始成为了默认存储引擎

1.1.1 连接器

首先,我们要先连接到这个数据库,连接器就是负责与客户端建立连接、获取权限、维持和管理连接连接命令如下:

mysql -h i p − p ip -p ippport -u$user -p

输入完命令后,我们在交互中输入密码,也可以直接将密码跟在 -p 后面写在命令中(不推荐)

连接命令中的 MySQL 是客户端工具,用来与服务端建立连接。在完成 TCP 握手后,连接器就会对输入的用户名和密码进行身份认证

如果输入的用户名或者密码不对,就会收到一个 Access denied for user 的报错,然后客户端程序结束执行。

认证通过后,连接器会到权限表里面查出来该用户拥有的权限,之后连接内的权限都会依赖于这个时候读取到的权限,也就意味着如果一个用户成功连接 MySQL,即使对用户权限进行了修改,也不会影响已经成功连接的权限,重新连接后才会使用新的权限

连接完成后如果没有后续操作的话,这个连接就处于空闲状态,我们可以使用以下命令来看到它

show processlist;

如下图所示:

如果 Command 列为 sleep 的就代表着空闲的连接

客户端如果长时间没有操作,连接器会在超时后自动断开连接,超时时间由参数 wait_timeout 控制,默认 8 小时。
查看等待超时时间命令:

show variables like ‘wait_timeout’

连接断开后客户端再发送请求的话,会受到报错:Lost connection to MySQL server during query,需要重连后再执行请求

在数据库中,长链接是指连接成功后,如果客户端持有请求,则一直使用同一个连接。短连接则是每次执行完较少的几次查询就断开连接,下次查询再重新建立一个新的连接

建立连接的过程通常是较为复杂的,我们最好使用长链接,尽量减少建立连接的动作

如果全部使用长链接的话,MySQL 可能占用内存会涨的比较快,这是因为 MySQL 在执行过程中临时使用的内存是管理在连接对象中的。这些资源会在连接断开的时候释放,如果长连接累积可能导致内存占用太大 OOM,被系统强行杀掉,这样的话表现出来就是 MySQL 异常重启了

解决这个问题的办法可以考虑以下方案:

  • 定期断开长连接,使用一段时间后,或者判断执行了一个占用内存大的查询后,断开连接,之后要查询再重连
  • 如果是 MySQL 5.7 或者更新版本,可以在执行一个比较大的操作后,通过执行 mysql_resert_connection 来重新初始化连接资源。这个过程不需要重连和重新进行权限认证,但是会将连接恢复到刚创建完的状态

1.1.2 查询缓存

建立完连接后,我们就可以执行 select 语句了。执行逻辑就会来到第二步:查询缓存

MySQL 拿到一个查询的请求后,会先到查询缓存中查看,之前是否执行过这条语句。之前执行过的语句及其结果可能会以 key-value 的形式,被直接缓存在内存中。key 是查询语句,value 是查询返回的结果。如果查询能够直接在缓存中找到这个 key,这个 value 就会直接返回给客户端

如果语句不在缓存中查询的话,就会继续后面的执行阶段。执行完操作后,执行结果会存入查询缓存中。如果查询命中缓存的话,MySQL 的执行效率很高。

但是多数情况下,我们不建议使用缓存,因为缓存往往弊大于利

查询缓存的失效很频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。对于更新较频繁的表来说,查询缓存的命中率很低,除非业务表是一张静态表,很久才更新一次,比如系统配置表,这种的查询才比较适合使用缓存

MySQL 提供了可选择是否使用缓存的方式,我们将参数 query_cache_type 设置为 DEMAND,这样对于默认的 SQL 语句都不会使用缓存,想要使用的话用 SQL_CACHE 显示的指定,例如下面的 SQL

SELECT SQL_CACHE * FROM test WHERE ID = 1;

注意!!!MySQL 8 版本以后都没有这个功能了!!!

1.1.3 分析器

如果没有命中查询缓存,就要真正的执行 SQL 语句了,MySQL 会先对 SQL 语句进行解析

分析器会先对我们的 SQL 进行词法分析,我们输入的是由多个字符串和空格组成的一条 SQL,MySQL 需要识别 SQL 中的字符串分别是什么,又代表什么

MySQL 从我们输入的 SELECT 这个关键字识别出来这是一个查询语句,把字符串 test 识别成表名 test,把字符串 ID 识别为 列 ID

词法分析完成后,就要做语法分析,根据词法分析的结果,语法分析器根据语法规则,判断输入的 SQL 语句是否满足 MySQL 语法

如果 SQL 语法不对,会收到 You have an error in your SQL syntax 的报错提示,提示一般是第一个出现错误的位置,下面是个错误示例:

查询的 SELECT 少一个字母 E

SLECT * FROM test WHERE ID = 1;

1.1.4 优化器

经过了分析器,MySQL 在执行前还要经过优化器的处理

优化器是在表里有多个索引的时候,决定使用哪个索引。或者在一个表有多表关联查询的时候,决定表的连接顺序

例如下面的 SQL,执行两个表的 join:

SELECT * FROM test1 join test2 using(ID) WHERE test1.a = 2 AND test2.b = 3;

这条 SQL 既可以先从 test1 表里面取出 a = 2 的记录的 ID,在根据 ID 值关联到 test2 表,再判断 test2 里面的 b 的值是否等于 3。

也可以从表 test2 里面取出来 b = 3 的记录的 ID,然后再用 ID 值关联到 test1,再判断 test1 里面的 a 的值是否等于 2。

两种方法执行的结果是一样的,但执行的效率可能不同,优化器的作用就是决定选择哪一种方案执行

优化器阶段完成后,SQL 语句的执行方案就确定下来了,然后进入执行器阶段

1.1.5 执行器

通过分析器知道要做什么,通过优化器知道该怎么做,进入执行阶段开始执行语句

执行开始会先判断对表 test 是否有查询的权限,如果没有就会返回没有权限的报错,如下:

SELECT * FROM test WHERE ID = 1;

ERROR 1142(42000): SELECT command denied to user ‘b’@‘localhost’ for table ’test’

有权限就继续执行,打开表的时候,执行器根据表的引擎定义,去使用这个引擎提供的接口。

比如我们这个例子中的表 test,ID 字段没有索引的话,执行器的执行流程是这个样的:

  1. 调用 InnoDB 引擎接口获取表的第一行,判断 ID 是不是 1,如果不是则跳过,如果是的话将这行存到结果集中
  2. 调用引擎接口获取下一行,重复判断逻辑,直到表的最后一行
  3. 执行器将上述遍历中所有满足条件的行组成的记录作为结果集返回给客户端

一个 SQL 语句就这么执行完成了

如果有索引的话,执行逻辑也类似,第一次调用的是 取满足条件的第一行 这个接口,之后循环这个接口,这些接口都是引擎中已经定义好的

我们可以在数据库的慢查询日志中看到一个 rows_examined 的字段,这个标识语句在执行过程中扫描了多少行,这个值是在执行器每次调用引擎获取数据行的时候累加的。要注意引擎扫描行数跟 rows_examined 并不是完全相同的

小结

以上介绍了一下 MySQL 的逻辑架构,让我们对一个 SQL 语句完整的执行流程的各个阶段有了初步的认知,后续的章节中再继续介绍 MySQL 的其它知识

问题

提出一个问题,如果表 test 中没有字段 c,当你执行了 SQL 语句 SELECT * FROM test WHERE c = 1;,那肯定会报错 Unknown column ‘c’ in where clause,这个错误是从上面提到的哪个阶段报出来的呢?

从头开始搞懂mysql(08)count(*)很慢怎么办(代码片段)

在开发系统的时候,我们可能经常需要计算一个表的行数,我们一般都是用selectcount(*)fromtable来查询,如果系统中记录越来越多,这条SQL也越来越慢。为什么MySQL不直接记录一个总数,每次要查询的时候直接读... 查看详情

从头开始搞懂mysql(03)事务隔离性(代码片段)

1、什么是事务?提到事务,我们肯定不陌生,和数据库打交道的时候,我们肯定会用到事物经典案例就是转账,小孙要给小白转100块钱,现在小孙的卡里只有100块钱。转账过程具体到程序里会有一系列操... 查看详情

从头开始搞懂mysql(05)行锁表锁全局锁(代码片段)

1、行锁行锁是针对数据表中行记录的锁,MySQL的行锁是在引擎层实现的,并不是所有的引擎都支持行锁,比如MyISAM就不支持,InnoDB支持行锁,避免了并发控制时使用表锁1.1两阶段锁在InnoDB事务中,行锁是在... 查看详情

mysql之sql语句执行过程(代码片段)

...SQL的执行命令之后底层是如何执行的呢,从这篇文章开始我们开始揭开MySQL底层执行过程的面纱。首选创建表createtab 查看详情

mysql之sql语句执行过程(代码片段)

...SQL的执行命令之后底层是如何执行的呢,从这篇文章开始我们开始揭开MySQL底层执行过程的面纱。首选创建表createtab 查看详情

从头开始搞懂mysql(04)索引(代码片段)

...下,我们想要找到一本书里面的内容,可能需要从头开始翻很久才能找到,有了目录之后,我们才能轻易找到想要的内容1.1常见索引结构哈希哈希是一种以(key-value)键值对存储的数据结构,哈希结构... 查看详情

一条查询sql是如何执行的(代码片段)

@toc执行如下SQL,我们看到的只是输入一条语句,返回一个结果,却不知道这条语句在MySQL内部的执行过程。select*fromwhereid=1;上图给出的是MySQL的基本架构示意图,从中你可以清楚地看到SQL语句在MySQL的各个功能模块中的执行过程。... 查看详情

mysql之sql语句执行过程(代码片段)

...SQL的执行命令之后底层是如何执行的呢,从这篇文章开始我们开始揭开MySQL底层执行过程的面纱。首选创建表createtableuser_info( idintnotnullprimarykey,usernamevarchar(16)notnull,cityvarchar(16)notnull,salaryintnotnulldefault0,createtimetimestampnotnulldefaul... 查看详情

一条sql查询语句是如何执行的(代码片段)

一条SQL查询语句是如何执行的下面是MySql的基本架构示意图,从图中可以清楚地看到SQL语句在MySQL的各个功能模块中的执行过程。大体来讲,MySQL可以分为Server层和存储引擎层两部分。Server层Server层包括连接器、查询缓存、分析器... 查看详情

mysql一条sql的执行过程详解(代码片段)

...交道,一天能写上几十条SQL语句,但你知道我们的系统是如何和数据库交互的吗?MySQL如何帮我们存储数据、又是如何帮我们管理事务?....是不是感觉真的除了写几个「select*fromdual」外基本脑子一片空白?这篇文章就将带你走进... 查看详情

mysql数据库详解一条sql更新语句是如何执行的?(代码片段)

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

输入的查询sql语句,是如何执行的?

...的执行过程。本文分享自华为云社区《​​一条查询SQL是如何执行的​​》,作者:共饮一杯无。执行如下SQL,我们看到的只是输入一条语句,返回一个结果,却不知道这条语句在MySQL内部的执行过程。select*fromwhereid=1;上图给出... 查看详情

一条sql语句是如何执行的

一条SQL语句是如何执行的SQL查询语句select*fromuserwhereID=10;MySQL的基本架构可以分为Server层和存储引擎两部分。Server层又包含连接器、(查询缓存)、分析器、优化器和执行器。**连接器:**连接器负责和客户端建立连接、获取权限 查看详情

mysql性能优化mysql中sql语句是如何执行的?(代码片段)

该篇章将开始整理MySQL的优化,不过开始之前,我们想了解清楚那就是MySQL是怎么执行的。文章目录1.MySQL驱动2.应用系统数据库连接池3.MySQL数据库连接池4.SQL执行过程4.1.线程监听:监听网络请求中的SQL语句4.2.SQL接口&#x... 查看详情

一条sql查询语句是如何执行的?(代码片段)

MySQL是典型的C/S架构(客户端/服务器架构),客户端进程向服务端进程发送一段文本(MySQL指令),服务器进程进行语句处理然后返回执行结果。问题来了。服务器进程对客户端发送的请求究竟做了什么处... 查看详情

201|基础架构:一条sql查询语句是如何执行的?(代码片段)

你好,我是林晓斌。这是专栏的第一篇文章,我想来跟你聊聊MySQL的基础架构。我们经常说,看一个事儿千万不要直接陷入细节里,你应该先鸟瞰其全貌,这样能够帮助你从高维度理解问题。同样,对于MySQL的学习也是这样。平... 查看详情

大白话系列mysql学习总结之初步了解mysql的架构设计(代码片段)

...,就能从数据库连接池获取一个数据库连接,然后就可以执行增删改查的操作了。可是我们并不知道MySQL里面是怎么执行的,例如我们执行了insert语句,只知道表里面多了一条数据,MySQL是怎么插入的我们就不知道了。二、解开My... 查看详情

mysql之系列

MySQL之如何在Linux环境安装配置。MySQL之零碎知识点。MySQL之常见面试题分析。01基础架构:一条SQL查询语句是如何执行的。02日志系统一条SQL更新语句是如何执行的?。03事务隔离:为什么你改了我还看不见?。04MyS... 查看详情