一条SQL语句的完整执行流程
MySQL 执行流程是怎样的?
先来一个上帝视角图,下面就是 MySQL 执行一条 SQL 查询语句的流程,也从图中可以看到 MySQL 内部架构里的各个功能模块。

可以看到, MySQL 的架构共分为两层:Server 层和存储引擎层
Server 层负责建立连接、分析和执行 SQLMySQL 大多数的核心功能模块都在这实现,主要包括连接器,查询缓存、解析器、预处理器、优化器、执行器等。另外,所有的内置函数(如日期、时间、数学和加密函数等)和所有跨存储引擎的功能(如存储过程、触发器、视图等。)都在 Server 层实现。
存储引擎层负责数据的存储和提取。支持 InnoDB、MyISAM、Memory 等多个存储引擎,不同的存储引擎共用一个 Server 层。现在最常用的存储引擎是 InnoDB,从 MySQL 5.5 版本开始, InnoDB 成为了MySQL 的默认存储引擎。我们常说的索引数据结构,就是由存储引擎层实现的,不同的存储引擎支持的索引类型也不相同,比如 InnoDB 支持索引类型是 B+树 ,且是默认使用,也就是说在数据表中创建的主键索引和二级索引默认使用的是 B+ 树索引。
好了,现在我们对 Server 层和存储引擎层有了一个简单认识,接下来,就详细说一条 SQL 查询语句的执行流程,依次看看每一个功能模块的作用。
我们以MySQL为例,一条SQL语句的执行流程大致如下:
连接器:管理连接,权限验证
查询缓存:如果启用缓存,则检查缓存,如果缓存命中则直接返回结果
分析器:词法分析,语法分析
优化器:生成执行计划,选择索引等
执行器:操作引擎,返回结果
存储引擎:存储数据,提供读写接口
步骤1:连接器
客户端与数据库建立连接,连接器负责身份认证和权限校验。
如果用户名密码认证通过,连接器会到权限表里面查出你拥有的权限。之后,这个连接里面的权限判断逻辑,都将依赖于此时读到的权限。
连接完成后,如果没有后续动作,这个连接就处于空闲状态,可以通过
show processlist命令查看。如果太长时间没有动静,连接器就会自动将它断开,由参数
wait_timeout控制,默认8小时。
步骤2:查询缓存(MySQL 8.0已移除)
在MySQL 8.0之前,如果启用了查询缓存,那么执行查询之前会先检查缓存。
缓存是以Key-Value形式存储的,Key是查询语句,Value是结果集。如果缓存命中,则直接返回结果。
但是,对于更新频繁的表,查询缓存往往弊大于利,因为每次更新都会清空缓存。所以MySQL 8.0已经移除了查询缓存。
步骤3:分析器
如果没有命中缓存,就开始真正执行语句了。首先,分析器会做词法分析,识别SQL语句中的字符串是什么,代表什么。比如,识别出
SELECT,表名,列名等。然后进行语法分析,根据语法规则判断这个SQL语句是否满足MySQL语法。如果不对,就会收到
You have an error in your SQL syntax的错误提示。
步骤4:优化器
经过分析器,MySQL知道你要做什么了。在开始执行之前,还要经过优化器的处理。
优化器是在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序等。
优化器阶段完成后,这个语句的执行方案就确定下来了。
步骤5:执行器
开始执行前,先判断一下你对这个表有没有执行查询的权限,如果没有,就会返回没有权限的错误。
如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。
执行流程(以SELECT为例):
调用存储引擎的接口取这个表的第一行,判断条件是否满足,满足则放到结果集中,不满足则跳过。
调用存储引擎接口取下一行,重复相同的判断逻辑,直到取到这个表的最后一行。
执行器将上述遍历过程中所有满足条件的行组成记录集返回给客户端。
步骤6:存储引擎
存储引擎负责数据的存储和提取。常用的存储引擎有InnoDB、MyISAM等。
执行器通过调用存储引擎的接口来获取数据。
下面以UPDATE为例,简要说明:
执行器先找引擎取需要更新的数据行,如果数据行在内存中则直接返回,否则从磁盘读入内存。
执行器对数据进行更新操作,然后调用引擎的写入接口。
引擎将数据更新到内存,同时将更新操作记录到redo log,此时redo log处于prepare状态。
执行器生成这个操作的binlog,并将binlog写入磁盘。
执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交(commit)状态,更新完成。
这就是一条SQL语句的完整执行流程。
