从输入 SQL 到返回数据,到底发生了什么?
当我们输入 SQL 到 MySQL 返回数据,这中间到底发生了什么呢?今天我们就来扒一扒这里面的细节。
SQL 执行流程
其实一个 SQL 从输入到返回数据,其过程大致为:建立连接、分析 SQL、优化 SQL、执行 SQL。
建立连接
当我们发送 SQL 给 MySQL 之前,我们都会输入账号和密码,从而与 MySQL 建立连接。这部分的工作,其实就是 MySQL 的连接器处理的。连接器负责跟客户端建立连接、获取权限、维持和管理连接。
当我们用管理员账号对账号权限做修改后,不影响已经存在的连接的权限,只有新建的连接才会使用新的权限设置。我们可以通过 show processlist
命令查看目前的连接情况,如下图所示。
上图中的 Command 列显示 Sleep 有好几个空闲的连接。如果客户端太久没动静,连接器就会自动断开,这个参数由 wait_timeout 控制,默认是 8 小时。
分析 SQL
在 MySQL 8.0 版本之前,MySQL 拿到一个查询请求后,会先到查询缓存中看看是否有查过。如果有,那么直接返回缓存的结果。但在 8.0 版本之后,查询缓存功能直接被删除了。主要是因为查询缓存弊大于利。
因为只要对一个表进行更新,这个表上的查询缓存就会被清空。可能你刚刚把结果缓存起来了,一个更新操作一来,这些缓存就全部失效了。所以查询缓存适合那些更新不频繁的表,用来提高查询效率。
当拿到 SQL 之后,MySQL 会对 SQL 进行词法分析和语法分析。词法分析会解析每个词的含义,而语法分析则是解析语法是否准确,分析器先会做词法分析,再做语法分析。
你输入的是由多个字符串和空格组成的一条 SQL 语句,MySQL 需要识别出里面的字符串分别是什么,代表什么。例如:select 表示查询,t 表示 t 这个表,字符串 ID 识别成列 ID。做完词法分析之后,就会做语法分析。
根据词法分析的结果,语法分析器会根据语法规则,判断输入的 SQL 语句是否满足 MySQL 语法。如果不满足语法,会有「You have an error in your SQL syntax」的错误提醒。
优化 SQL
经过分析器,MySQL 就知道你要做什么了。但在开始执行之前,还要先经过优化器的处理。优化器是在表里面有多个索引的时候,决定使用哪个索引。或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序。
有时候两种执行方法的逻辑结果是一样的,但是执行的效率会有不同,而优化器的作用就是决定选择使用哪一个方案。优化器阶段完成后,这个语句的执行方案就确定下来了,然后进入执行器阶段。
执行 SQL
MySQL 通过分析器知道了你要做什么,通过优化器知道了该怎么做,于是就进入了执行器阶段,开始执行语句。开始执行的时候,要先判断一下你对这个表 T 有没有执行查询的权限,如果没有,就会返回没有权限的错误。
如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。例如对于 select * from T where ID=10;
这条语句,ID 字段没有索引,那么执行器的执行流程是这样的:
调用 InnoDB 引擎接口取这个表的第一行,判断 ID 值是不是 10,如果不是则跳过,如果是则将这行存在结果集中。 调用引擎接口取「下一行」,重复相同的判断逻辑,直到取到这个表的最后一行。 执行器将上述遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。
至此,这个语句就执行完成了。
对于有索引的表,执行的逻辑也差不多。第一次调用的是「取满足条件的第一行」这个接口,之后循环取「满足条件的下一行」这个接口,这些接口都是引擎中已经定义好的。
你会在数据库的慢查询日志中看到一个 rows_examined
的字段,表示这个语句在执行器执行过程中扫描了多少行。这个值就是在执行器每次调用引擎获取数据行的时候累加的。在有些场景下,执行器调用一次,在引擎内部则扫描了多行,因此引擎扫描行数跟 rows_examined 并不是完全相同的。
MySQL 技术架构
其实上面的过程,就是按着 MySQL 的技术架构来的,其技术架构如下图所示。
大体来说,MySQL 技术架构可以分为 Server 层和存储引擎层两部分。
Server 层负责建立连接、分析 SQL 等功能。 所有跨存储引擎的功能都在这一层实现,例如存储过程、触发器、视图等。
存储引擎层负责数据的存储和提取。 其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。现在最常用的是 InnoDB 存储引擎,从 MySQL 5.5.5 开始成为了默认的存储引擎。
InnoDB 存储引擎
目前使用最广泛的是 InnoDB 存储引擎,其体系架构分为三大块,分别是:后台线程、内存池、文件,其体系架构如下图所示。
在上图中,后台线程负责刷新内存池的数据,内存池负责缓存磁盘的数据,文件则是具体的数据存储。
后台线程的主要工作是负责刷新内存池的数据,保证缓冲池中的内存缓存的是最近的数据。InnoDB 存储引擎是多线程的模型,因此其后台有多个不同的后台线程,负责处理不同的任务。目前有 4 种不同类型的处理线程,分别是:Master Tread、IO Thread、Purge Thread、Page Cleaner Thread。
内存池是 InnoDB 所管理内存的统称,主要用于缓存磁盘数据,从而加快数据的读取。根据其用途不同,内存池还可以分为:缓冲池、重做日志缓冲、额外内存池三大块。
文件则是最终存取数据库数据的地方,其存储了包括索引文件、数据文件等相关的数据文件。
总结
最后我们总结一下一条 SQL 语句从查询到返回数据的 5 个阶段,分别是:
建立连接。客户端会首先与 MySQL 建立 TCP 连接,在连接器中会进行连接管理、权限验证等操作。 分析 SQL。分析器进行词法、语法分析,词法分析知道要查询什么内容,语法分析判断语法是否有问题。 优化 SQL。优化器根据 SQL 情况,判断使用哪种执行方式更好,例如使用哪个索引,哪种表连接方式。 执行 SQL。根据优化器的优化结果,生成执行计划,执行器调用存储引擎的 API 来执行查询,最终将数据返回给客户端。