文章目录
-
- [0. MySQL 的宏观架构:概览](#0. MySQL 的宏观架构:概览)
- [1. 第一步:连接器------建立连接](#1. 第一步:连接器——建立连接)
- [2. 第二步:查询缓存------历史功能](#2. 第二步:查询缓存——历史功能)
- [3. 第三步:解析器------语法解析](#3. 第三步:解析器——语法解析)
- [4. 第四步:优化器------查询优化](#4. 第四步:优化器——查询优化)
- [5. 第五步:执行器------查询执行](#5. 第五步:执行器——查询执行)
- [6. 第六步:存储引擎------数据存储](#6. 第六步:存储引擎——数据存储)
- 总结:完整流程

一条简单的 SQL语句
SELECT * FROM user WHERE id = 1;当按下回车键的那一刻,MySQL 内部究竟发生了什么?它是如何找到数据的?为什么有时候加了索引还是慢?
这篇笔记,从连接、缓存、解析、优化、执行 到存储引擎,全流程还原一条查询语句的流程。
0. MySQL 的宏观架构:概览
在深入细节之前,需要先了解 MySQL 的基础架构图。MySQL 可以大致分为两层:
- Server 层(大脑):涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等)。所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。
- 存储引擎层(手脚) :负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。目前最常用的默认引擎是 InnoDB。
核心流程概览:
连接器 → \rightarrow → 查询缓存(MySQL 8.0前) → \rightarrow → 分析器 → \rightarrow → 优化器 → \rightarrow → 执行器 → \rightarrow → 存储引擎
1. 第一步:连接器------建立连接
执行 SQL 时,第一件事是连接到数据库。
- 职责:负责与客户端建立连接、获取权限、维持和管理连接。
- 交互流程 :
- TCP 握手:基于 TCP/IP 协议建立物理连接。
- 身份认证:验证用户名和密码。
- 权限获取 :一旦认证通过,连接器会到权限表中查询对应的权限。需要注意的是,连接建立后,即使管理员修改了权限,在当前连接断开前也不会生效。
- 长连接与短连接 :
- 通常建议使用长连接以减少握手开销。
- 潜在问题:MySQL 在执行过程中临时使用的内存由连接对象管理。长连接累积会导致内存占用过大,可能被系统强行终止(OOM)。
- 解决方案:定期断开长连接或初始化连接资源。
2. 第二步:查询缓存------历史功能
注意:此功能主要存在于 MySQL 8.0 之前。MySQL 8.0 版本已正式删除了查询缓存。
- 逻辑 :连接建立后,MySQL 会先查询缓存。如果之前执行过相同的语句,结果会以
Key-Value的形式直接缓存在内存中(Key 是 SQL 语句,Value 是查询结果)。 - 命中:如果命中,直接返回结果,速度极快。
- 弊端(被废弃的原因) :
- 失效频繁 :只要对一个表进行更新操作(Insert/Update/Delete),该表上所有的查询缓存都会被清空。对于更新频繁的数据库,缓存几乎无用且增加了额外开销。
- 适用场景有限:仅对静态数据表有效。
3. 第三步:解析器------语法解析
如果没有命中缓存,或者缓存功能不存在,SQL 就真正开始进入执行流程。首先要解决的问题是:解析 SQL 语句的意图。
这一步分为两个阶段:
SQL语句可以算是一种编程语言(结构化S 查询Q 语言L ),所以一般编译器具有的功能,SQL的解析器也需要具备
-
词法分析:
- 将输入的 SQL 文本字符串,识别出各个组成部分及其含义。
- 例如:将
SELECT识别为查询关键字 ,将user识别为表名,将id识别为列名。
-
语法分析:
- 根据词法分析的结果,判断 SQL 语句是否满足 MySQL 的语法规则。
- 错误产生 :常见的语法错误提示
You have an error in your SQL syntax就是在此阶段产生。
预处理器 :
在某些架构划分中,分析器之后还存在一个预处理阶段,用于检查表名、列名是否存在,消除字段歧义,并进行权限验证。
4. 第四步:优化器------查询优化
经过分析器,MySQL 明确了要执行的操作。但在开始执行之前,需要经过优化器的处理。这是影响 MySQL 性能的关键环节。
-
职责:决定使用哪个索引,或者在多表关联时,决定各个表的连接顺序。
-
例如:
sqlSELECT * FROM t1 JOIN t2 USING(ID) WHERE t1.c = 10 AND t2.d = 20;- 方案 A:先从 t1 查出 c=10 的记录,通过 ID 关联到 t2,再判断 t2.d=20。
- 方案 B:先从 t2 查出 d=20 的记录,通过 ID 关联到 t1,再判断 t1.c=10。
- 优化器的作用:通过基于成本的评估,选择执行效率最高的方案。
-
产出 :生成执行计划。
基于 explain 关键字,可以查询 SQL执行计划
5. 第五步:执行器------查询执行
优化器选定方案后,将任务交给执行器。
- 权限复核:执行器在执行前会再次确认对相关表是否具有查询权限(如果在预处理阶段未检查)。
- 调用引擎接口 :执行器本身不存储数据,通过调用存储引擎提供的 API 接口来获取数据。
以 SELECT * FROM user WHERE id = 1; 为例(假设 id 字段没有索引):
- 调用 InnoDB 引擎接口取该表的第一行。
- 判断 ID 是否为 1,如果不是则跳过,如果是则将该行存入结果集。
- 调用引擎接口取下一行。
- 重复相同的判断逻辑,直到取到表的最后一行。
- 执行器将所有满足条件的记录组成结果集,返回给客户端。
说明 :在数据库慢查询日志中看到的
rows_examined(扫描行数),就是在执行器每次调用引擎获取数据行时累加的。
6. 第六步:存储引擎------数据存储
这是数据的最终存储层。执行器调用的 API 最终由存储引擎实现。
- InnoDB(默认) :
- 数据按聚簇索引(B+树)结构存储。
- 如果查询使用索引,直接在 B+ 树上进行搜索,速度很快。
- 如果不使用索引(全表扫描),InnoDB 需要将所有数据页加载到内存(Buffer Pool)中供执行器逐行判断。
总结:完整流程
回顾 SELECT * FROM user WHERE id = 1 的执行过程:
- 连接器:验证身份,分配连接线程。
- 查询缓存(MySQL 8.0前):检查是否有缓存结果,有则直接返回,无则继续。
- 分析器:识别语句为查询操作,检查语法正确性。
- 优化器 :发现
id字段有索引,决定使用主键索引进行查询,生成执行计划。 - 执行器:调用 InnoDB 引擎的数据获取接口。
- InnoDB:在 B+ 树索引中定位到 id=1 的行,返回给执行器。
- 执行器:接收数据,返回给客户端。
总结:
了解SQL语句的执行流程,对于SQL 性能调优有一定的帮助,比如:
- 明白优化器是基于成本进行选择,就能理解为何有时 MySQL 会选错索引
- 了解执行器 与存储引擎的交互方式,就能知道全表扫描效率低下的原因
- 等等