MySQL架构总览_查询执行流程_SQL解析顺序

news/2025/1/21 10:16:55/文章来源:https://www.cnblogs.com/TMesh/p/18683075

目录
  • MySQL 架构总览
  • 查询执行流程
    • 连接
    • 处理
    • 结果
  • SQL 解析顺序
    • 准备工作
    • FROM
    • WHERE
    • GROUP BY
    • HAVING
    • SELECT
    • ORDER BY
    • LIMIT
  • 总结
  • 参考书籍

MySQL 架构总览

架构最好看图,再配上必要的说明文字。
下图根据参考书籍中一图为原本,再在其上添加上了自己的理解。
[Pasted image 20241121153439.png]
从上图中我们可以看到,整个架构分为两层,上层是 MySQLD 的被称为的 ‘SQL Layer’,下层是各种各样对上提供接口的存储引擎,被称为 ‘Storage Engine Layer’。其它各个模块和组件,从名字上就可以简单了解到它们的作用,这里就不再累述了。

查询执行流程

连接

  1. 客户端发起一条 Query 请求,监听客户端的‘连接管理模块’接收请求
  2. 将请求转发到‘连接进/线程模块’
  3. 调用‘用户模块’来进行授权检查
  4. 通过检查后,‘连接进/线程模块’从‘线程连接池’中取出空闲的被缓存的连接线程和客户端请求对接,如果失败则创建一个新的连接请求

处理

  1. 先查询缓存,检查 Query 语句是否完全匹配,接着再检查是否具有权限,都成功则直接取数据返回
  2. 上一步有失败则转交给‘命令解析器’,经过词法分析,语法分析后生成解析树
  3. 接下来是预处理阶段,处理解析器无法解决的语义,检查权限等,生成新的解析树
  4. 再转交给对应的模块处理
  5. 如果是 SELECT 查询还会经由‘查询优化器’做大量的优化,生成执行计划
  6. 模块收到请求后,通过‘访问控制模块’检查所连接的用户是否有访问目标表和目标字段的权限
  7. 有则调用‘表管理模块’,先是查看 table cache 中是否存在,有则直接对应的表和获取锁,否则重新打开表文件
  8. 根据表的 meta 数据,获取表的存储引擎类型等信息,通过接口调用对应的存储引擎处理
  9. 上述过程中产生数据变化的时候,若打开日志功能,则会记录到相应二进制日志文件中

结果

  1. Query 请求完成后,将结果集返回给‘连接进/线程模块’
  2. 返回的也可以是相应的状态标识,如成功或失败等
  3. ‘连接进/线程模块’进行后续的清理工作,并继续等待请求或断开与客户端的连接
    一图小总结
    [701942-20151210224221011-1559007674.png]

SQL 解析顺序

接下来再走一步,让我们看看一条 SQL 语句的前世今生。
首先看一下示例语句

SELECT DISTINCT< select_list >
FROM< left_table > < join_type >
JOIN < right_table > ON < join_condition >
WHERE< where_condition >
GROUP BY< group_by_list >
HAVING< having_condition >
ORDER BY< order_by_condition >
LIMIT < limit_number >

然而它的执行顺序是这样的

FROM <left_table>
ON <join_condition>
<join_type> JOIN <right_table>
WHERE <where_condition>
GROUP BY <group_by_list>
HAVING <having_condition>
SELECT
DISTINCT <select_list>
ORDER BY <order_by_condition>
LIMIT <limit_number>

虽然自己没想到是这样的,不过一看还是很自然和谐的,从哪里获取,不断的过滤条件,要选择一样或不一样的,排好序,那才知道要取前几条呢。
既然如此了,那就让我们一步步来看看其中的细节吧。

准备工作

  1. 创建测试数据库
create database testQuery
  1. 创建测试表
CREATE TABLE table1
(uid VARCHAR(10) NOT NULL,name VARCHAR(10) NOT NULL,PRIMARY KEY(uid)
)ENGINE=INNODB DEFAULT CHARSET=UTF8;CREATE TABLE table2
(oid INT NOT NULL auto_increment,uid VARCHAR(10),PRIMARY KEY(oid)
)ENGINE=INNODB DEFAULT CHARSET=UTF8;
  1. 插入数据
INSERT INTO table1(uid,name) VALUES('aaa','mike'),('bbb','jack'),('ccc','mike'),('ddd','mike');INSERT INTO table2(uid) VALUES('aaa'),('aaa'),('bbb'),('bbb'),('bbb'),('ccc'),(NULL);
  1. 最后想要的结果
SELECTa.uid,count(b.oid) AS total
FROMtable1 AS a
LEFT JOIN table2 AS b ON a.uid = b.uid
WHEREa. NAME = 'mike'
GROUP BYa.uid
HAVINGcount(b.oid) < 2
ORDER BYtotal DESC
LIMIT 1;

FROM

当涉及多个表的时候,左边表的输出会作为右边表的输入,之后会生成一个虚拟表 VT1。
(1-J1)笛卡尔积
计算两个相关联表的笛卡尔积(CROSS JOIN) ,生成虚拟表 VT1-J1。

mysql> select * from table1,table2;
+-----+------+-----+------+
| uid | name | oid | uid  |
+-----+------+-----+------+
| aaa | mike |   1 | aaa  |
| bbb | jack |   1 | aaa  |
| ccc | mike |   1 | aaa  |
| ddd | mike |   1 | aaa  |
| aaa | mike |   2 | aaa  |
| bbb | jack |   2 | aaa  |
| ccc | mike |   2 | aaa  |
| ddd | mike |   2 | aaa  |
| aaa | mike |   3 | bbb  |
| bbb | jack |   3 | bbb  |
| ccc | mike |   3 | bbb  |
| ddd | mike |   3 | bbb  |
| aaa | mike |   4 | bbb  |
| bbb | jack |   4 | bbb  |
| ccc | mike |   4 | bbb  |
| ddd | mike |   4 | bbb  |
| aaa | mike |   5 | bbb  |
| bbb | jack |   5 | bbb  |
| ccc | mike |   5 | bbb  |
| ddd | mike |   5 | bbb  |
| aaa | mike |   6 | ccc  |
| bbb | jack |   6 | ccc  |
| ccc | mike |   6 | ccc  |
| ddd | mike |   6 | ccc  |
| aaa | mike |   7 | NULL |
| bbb | jack |   7 | NULL |
| ccc | mike |   7 | NULL |
| ddd | mike |   7 | NULL |
+-----+------+-----+------+
28 rows in set (0.00 sec)

(1-J2)ON 过滤
基于虚拟表 VT1-J1 这一个虚拟表进行过滤,过滤出所有满足 ON 谓词条件的列,生成虚拟表 VT1-J2。
注意:这里因为语法限制,使用了 'WHERE' 代替,从中读者也可以感受到两者之间微妙的关系;

mysql> SELECT-> *-> FROM-> table1,-> table2-> WHERE-> table1.uid = table2.uid-> ;
+-----+------+-----+------+
| uid | name | oid | uid  |
+-----+------+-----+------+
| aaa | mike |   1 | aaa  |
| aaa | mike |   2 | aaa  |
| bbb | jack |   3 | bbb  |
| bbb | jack |   4 | bbb  |
| bbb | jack |   5 | bbb  |
| ccc | mike |   6 | ccc  |
+-----+------+-----+------+
6 rows in set (0.00 sec)

(1-J3)添加外部列
如果使用了外连接(LEFT,RIGHT,FULL),主表(保留表)中的不符合 ON 条件的列也会被加入到 VT1-J2 中,作为外部行,生成虚拟表 VT1-J3。

mysql> SELECT-> *-> FROM-> table1 AS a-> LEFT OUTER JOIN table2 AS b ON a.uid = b.uid;
+-----+------+------+------+
| uid | name | oid  | uid  |
+-----+------+------+------+
| aaa | mike |    1 | aaa  |
| aaa | mike |    2 | aaa  |
| bbb | jack |    3 | bbb  |
| bbb | jack |    4 | bbb  |
| bbb | jack |    5 | bbb  |
| ccc | mike |    6 | ccc  |
| ddd | mike | NULL | NULL |
+-----+------+------+------+
7 rows in set (0.00 sec)

下面从网上找到一张很形象的关于 ‘SQL JOINS' 的解释图,如若侵犯了你的权益,请劳烦告知删除,谢谢。
[Pasted image 20241121154056.png]

WHERE

对 VT1 过程中生成的临时表进行过滤,满足 WHERE 子句的列被插入到 VT2 表中。
注意:
此时因为分组,不能使用聚合运算;也不能使用 SELECT 中创建的别名;
与 ON 的区别:
如果有外部列,ON 针对过滤的是关联表,主表(保留表)会返回所有的列;
如果没有添加外部列,两者的效果是一样的;
应用:
对主表的过滤应该放在 WHERE;
对于关联表,先条件查询后连接则用 ON,先连接后条件查询则用 WHERE;

mysql> SELECT-> *-> FROM-> table1 AS a-> LEFT OUTER JOIN table2 AS b ON a.uid = b.uid-> WHERE-> a. NAME = 'mike';
+-----+------+------+------+
| uid | name | oid  | uid  |
+-----+------+------+------+
| aaa | mike |    1 | aaa  |
| aaa | mike |    2 | aaa  |
| ccc | mike |    6 | ccc  |
| ddd | mike | NULL | NULL |
+-----+------+------+------+
4 rows in set (0.00 sec)

GROUP BY

这个子句会把 VT2 中生成的表按照 GROUP BY 中的列进行分组。生成 VT3 表。

  • 注意:
    • 其后处理过程的语句,如 SELECT,HAVING,所用到的列必须包含在 GROUP BY 中,对于没有出现的,得用聚合函数;
  • 原因:
    • GROUP BY 改变了对表的引用,将其转换为新的引用方式,能够对其进行下一级逻辑操作的列会减少;
  • 我的理解是:
    • 根据分组字段,将具有相同分组字段的记录归并成一条记录,因为每一个分组只能返回一条记录,除非是被过滤掉了,而不在分组字段里面的字段可能会有多个值,多个值是无法放进一条记录的,所以必须通过聚合函数将这些具有多值的列转换成单值;
mysql> SELECT-> *-> FROM-> table1 AS a-> LEFT OUTER JOIN table2 AS b ON a.uid = b.uid-> WHERE-> a. NAME = 'mike'-> GROUP BY-> a.uid;
+-----+------+------+------+
| uid | name | oid  | uid  |
+-----+------+------+------+
| aaa | mike |    1 | aaa  |
| ccc | mike |    6 | ccc  |
| ddd | mike | NULL | NULL |
+-----+------+------+------+
3 rows in set (0.00 sec)

HAVING

这个子句对 VT3 表中的不同的组进行过滤,只作用于分组后的数据,满足 HAVING 条件的子句被加入到 VT4 表中。

mysql> SELECT-> *-> FROM-> table1 AS a-> LEFT OUTER JOIN table2 AS b ON a.uid = b.uid-> WHERE-> a. NAME = 'mike'-> GROUP BY-> a.uid-> HAVING-> count(b.oid) < 2;
+-----+------+------+------+
| uid | name | oid  | uid  |
+-----+------+------+------+
| ccc | mike |    6 | ccc  |
| ddd | mike | NULL | NULL |
+-----+------+------+------+
2 rows in set (0.00 sec)

SELECT

这个子句对 SELECT 子句中的元素进行处理,生成 VT5 表。
(5-J1)SELECT
(5-J1)计算表达式 计算 SELECT 子句中的表达式,生成 VT5-J1
(5-J2)DISTINCT
寻找 VT5-1 中的重复列,并删掉,生成 VT5-J2
如果在查询中指定了 DISTINCT 子句,则会创建一张内存临时表(如果内存放不下,就需要存放在硬盘了)。这张临时表的表结构和上一步产生的虚拟表VT5是一样的,不同的是对进行 DISTINCT 操作的列增加了一个唯一索引,以此来除重复数据。

mysql> SELECT-> a.uid,-> count(b.oid) AS total-> FROM-> table1 AS a-> LEFT OUTER JOIN table2 AS b ON a.uid = b.uid-> WHERE-> a. NAME = 'mike'-> GROUP BY-> a.uid-> HAVING-> count(b.oid) < 2;
+-----+-------+
| uid | total |
+-----+-------+
| ccc |     1 |
| ddd |     0 |
+-----+-------+
2 rows in set (0.00 sec)

ORDER BY

从 VT5-J2 中的表中,根据 ORDER BY 子句的条件对结果进行排序,生成 VT6 表。
注意:
唯一可使用 SELECT 中别名的地方;

mysql> SELECT-> a.uid,-> count(b.oid) AS total-> FROM-> table1 AS a-> LEFT OUTER JOIN table2 AS b ON a.uid = b.uid-> WHERE-> a. NAME = 'mike'-> GROUP BY-> a.uid-> HAVING-> count(b.oid) < 2-> ORDER BY-> total DESC;
+-----+-------+
| uid | total |
+-----+-------+
| ccc |     1 |
| ddd |     0 |
+-----+-------+
2 rows in set (0.00 sec)

LIMIT

LIMIT 子句从上一步得到的 VT6 虚拟表中选出从指定位置开始的指定行数据。
注意:
offset 和 rows 的正负带来的影响;
当偏移量很大时效率是很低的,可以这么做:
采用子查询的方式优化,在子查询里先从索引获取到最大 id,然后倒序排,再取 N 行结果集
采用 INNER JOIN 优化,JOIN 子句里也优先从索引获取 ID 列表,然后直接关联查询获得最终结果

mysql> SELECT-> a.uid,-> count(b.oid) AS total-> FROM-> table1 AS a-> LEFT JOIN table2 AS b ON a.uid = b.uid-> WHERE-> a. NAME = 'mike'-> GROUP BY-> a.uid-> HAVING-> count(b.oid) < 2-> ORDER BY-> total DESC-> LIMIT 1;
+-----+-------+
| uid | total |
+-----+-------+
| ccc |     1 |
+-----+-------+
1 row in set (0.00 sec)

总结

[Pasted image 20241121154437.png]

参考书籍

《MySQL性能调优与架构实践》
《MySQL技术内幕:SQL编程》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/872685.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BAT批处理文件语法.061024

bat是dos下的批处理文件 .cmd是nt内核命令行环境的另一种批处理文件。 从更广义的角度来看,unix的shell脚本以及其它操作系统甚至应用程序中由外壳进行解释执行的文本,都具有与批处理文件十分相似的作用,而且同样是由专用解释器以行为单位解释执行,这种文本形式更通用的称谓…

AD中域用户密码策略不生效的解决方案.100301

每到一个月,AD就会提示修改密码,改就改吧,但是还提示一些乱七八糟的规则。 我把这些规则都禁用或是设为没有定义了,但还是报“不能和之前的0个密码相同”, 最后, 解决方案: 在域控制器服务器中: 运行以下指令: gpupdate /force好了。.zstitle { width: 280px; text-al…

金融波动率的多模型建模研究:GARCH族与HAR模型的Python实现与对比分析

金融资产波动率建模在现代金融工程中具有重要地位,其应用涵盖风险管理、衍生品定价和投资组合优化等核心领域。本文着重探讨三种主流波动率建模方法:广义自回归条件异方差模型(GARCH)、Glosten-Jagannathan-Runkle-GARCH模型(GJR-GARCH)以及异质自回归模型(HAR)。本文将系统阐…

「youlai-boot」入门篇:从0到1搭建 Java、Spring Boot、Spring Security 企业级权限管理系统

本文介绍了如何使用 Java 和 Spring Boot 3,从零开始构建开源的 youlai-boot 企业级权限管理系统,涵盖 MySQL、Redis 整合及 Spring Security,适合前端开发者和后端初学者。🚀 作者主页: 有来技术 🔥 开源项目: youlai-mall ︱vue3-element-admin︱youlai-boot︱vue-u…

SQL Server 2005部署备份任务.120308

环境:SQL Server 2005 任务: 1,每日凌晨1点给本地sql server做本地完整备份,且只保留7天的本地备份; 2,每日凌晨5点将本地的备份打包上传到存储服务器。 思路: 1,sql server2005不支持SQL Server2000的sqlmaint命令,所以,需要通过数据库维护计划进行备份,而不能一步…

[每日 B] Kevin and Geometry

前言 想着能做的题也不多, 直接当每日一练的形式写就好了 心态放平, 冷静利用时间 思路 转化题意 考虑一个等腰梯形的性质朴素的想法是, 枚举 \(b\) , 枚举 \(c < b\) , 然后计算是否有对应的 \(a\) 满足 \(\exists a, \exists a + 2c\) , 特判 \(c = 0\) 这样直接爆炸, 考虑…

【译】调查并确定 Windows 运行速度变慢问题

我最近注意到,我的 Windows 7 家用笔记本电脑运行速度比平时要慢。系统似乎在磁盘 I/O 上花了很多时间。当我查看任务管理器时,我发现笔记本电脑的缓存磁盘数据量大约只有平时的 五分之一,但问题的原因并不明显。 我的工作之一是性能专家——要调查各种日常或深层次的性能问…

SQLServer2005恢复Master库.110509

master库对于SQLServer来说,是很重要的系统数据库,保存着所有Sqlserver的用户信息、数据库信息等,当数据库崩溃时,master数据库的恢复成功与否起着重要的作用。这就跟Oracle的System表空间一样,非常的重要。 备份数据前期准备: (1)在备用机准备好和生产机器一样的sql20…

legged-robot关于locomotion、Navigation任务主要文章速览

0. 前言 目前legged robot包括locomotion(怎么走)、navigation(往哪走)、人形机器人的whole body control以及基于机械臂的manipulation的任务。 本文章特此记录 一方面便于日后自己的温故学习,另一方面也便于大家的学习和交流。 如有不对之处,欢迎评论区指出错误,你我共…

IT部门管理之IT十二条令.180409

团队越来越大,靠人管几乎有力无心,只能靠制度管理了。 前段时间对部门颁布了12条令,效果明显。 特此Mark。汇报:三条总结:汇报讲究精简,一个事情最多一句话概括。 一页报告:内容精简,报告一页word搞定。 统计分析:尽量用报表或图表说话。用数字说话,比用语言表达更加…

IDEA如何查看类中的方法和属性列表

前言 大家好,我是小徐啊。我们在开发Java应用的时候,一般都是需要写属性和方法的。这些属性和方法构成了我们代码开发的基础。当一个Java类中,有很多属性和方法的时候,如何快速找到这些方法和属性呢?今天,小徐就来介绍下如何在IDEA中查找。 如何查找属性和方法 首先,打开…

MySql 执行计划

目录获取执行计划解读执行计划type 字段Extra 字段访问谓词与过滤谓词完整字段信息格式化参数执行计划中的分区表信息获取额外的执行计划信息获取指定连接的执行计划获取实际运行的执行计划执行计划(execution plan,也叫查询计划或者解释计划)是 MySQL 服务器执行 SQL 语句的…