表面上一条简单查询sql 表面上看并不复杂,实际上内部执行了比较复杂的步骤。
简单看下MySQL 的基本架构示意:
MySQL 可以分为 Server 层和存储引擎层两部分。
不过存在最上层服务:
最上层服务并不是MYSQL所独有的,大多数基于网络的客户端/服务器的工具或者服务都有类似的架构。比如连接处理、授权认证、安全等等
------《高性能MySQL》
继续说回主要的2层部分。
1.Server层:大部分核心服务功能都在这层,包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。
2.存储引擎层:负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始成为了默认存储引擎。
存储引擎层不会去解析SQL【InnoDB例外,他会解析外键定义,因为MySQL服务器本身没有实现该功能】,不能存储引擎之间不会相互通信,而只是简单地响应上层服务器的请求。
好了,可以开始分析一条查询SQL的执行情况了。
比如: select * from table
始执行了。
一、连接器
首先,需要先连接到这个数据库上,这时候接待你的就是连接器。连接器负责跟客户端建立连接、获取权限、维持和管理连接。
mysql -h xxx -uxxx -P xxx -p xxxx
尽管可以在cli上直接输入密码,但是会有一定提示,不过建议在第一次命令中不输入密码,处于安全考虑。
连接命令中的 mysql 是客户端工具,用来跟服务端建立连接。在完成经典的 TCP 握手后,连接器就要开始认证你的身份,这个时候用的就是你输入的用户名和密码。
在验证密码是会出现什么情况呢?来个中文式的伪代码吧,毕竟程序员一天不写代码,心里难受
if( username == true && passport == true){
连接器会到权限表里面查出你拥有的权限。之后,这个连接里面的权限判断逻辑,都将依赖于此时读到的权限。
}else{
printf("Access denied for user");
exit();
}
备注:当验证通过后,因为此次进程(中途不断开连接情况),权限用的当时连接器验证的权限,尽管你在外面改了该用户连接权限,依然可以使用在这个进程继续使用该链接操作数据。所以你需要断开连接重新连接。
很多文章此时会提供一个MySQL5.7.0+才有的命令:mysql_reset_connection
mysql_reset_connection()具有类似于mysql_change_user()或 auto-reconnect 的效果,但连接未关闭并重新打开,并且未执行重新认证。
connection-related state 受影响如下:
1.回滚任何 active transactions 并重置自动提交模式。
2.所有 table 锁都被释放。
3.所有TEMPORARY
表都关闭(并删除)。
4.Session 系统变量重新初始化为相应的 global 系统变量的值,包括由 state(如设置名称)隐式设置的系统变量。
5.用户变量设置丢失。
6.已准备的 statements 已发布。
7.HANDLER变量已关闭。
8.LASTINSERT_ID()的值重置为 0。
9.使用GET_LOCK()获取的锁定被释放。
Return 值
成功归零。如果发生错误,则为非零。
--------来源于MySQL https://www.docs4dev.com/docs/zh/mysql/5.7/reference/mysql-reset-connection.html
二、查询缓存
连接建立完,就可以执行SQL了,真的是吗?
NO,他会先去查询缓存中look look,如果之前执行过该语句,那么他会以一种key-value
对的形式存储于内存中,key就是查询语句,value就是这个sql的结果集。命中缓存后,会直接把结果返回给客户端
小伙,缓存没命中,快给我结果,再不给我就超时了【提下:一般的mysql连接都是采用长连接哦】
啊 sir,别急好不好 还没说完呢。
查询缓存是可以按需操作的,query_cache_type 设置成 DEMAND,这样对于默认的 SQL 语句都不使用查询缓存。如果不想更改配置文件,只需这么操作
#用缓存(5.8以下应该都有用缓存的,尽管缓存弊大于利)
select sql_cache count(*) from table;
#不用缓存
select sql_no_cache count(*) from table;
需要注意的是,MySQL 8.0 版本直接将查询缓存的整块功能删掉了,也就是说 8.0 开始彻底没有这个功能了。
好了查询缓存任务完成,交给下一个兄弟了
三、分析器
分析器主要做的就是“词法分析”。
你输入的是由多个字符串和空格组成的一条 SQL 语句,MySQL 需要识别出里面的字符串分别是什么,代表什么。
MySQL 从你输入的"select"这个关键字识别出来,这是一个查询语句。它也要把字符串“T”识别成“表名 T”,把字符串“ID”识别成“列 ID”。做完了这些识别以后,就要做“语法分析”。
根据词法分析的结果,语法分析器会根据语法规则,判断你输入的这个 SQL 语句是否满足 MySQL 语法。如果你的语句不对,就会收到You have an error in your SQL syntax
的错误提醒,
比如下面这个语句 select 少打了开头的字母“s”。
mysql> elect * from table where id=1;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'elect * from table where id=1' at line 1
一般语法错误会提示第一个出现错误的位置,所以你要关注的是紧接“use near”的内容。
同时如果查询的字段或者where中的一个字段不在该表,那么也会在这个地方报错的。
四、优化器
优化器到底是干嘛的呢?简单的说
1.优化器是在表里面有多个索引的时候,决定使用哪个索引;
2.在一个语句有多表关联(join)的时候,决定各个表的连接顺序。
3.选择最合适的索引;
4.选择表扫还是走索引;
5.优化where子句了;
6.排除管理中无用的表;
7.决定 order by 和 group by 是否走索引;
8.尝试使用 inner join 替换 outer join;
9.简化子查询,决定结果缓存;
10.合并试图。
优化器阶段完成后,这个语句的执行方案就确定下来了,然后进入执行器阶段。
优化器并不关心表使用的是什么存储引擎,但存储引擎对于优化查询是有影响的。优化器会请求存储引擎提供容量或某个具体操作的开销信息,以及表数据的统计信息等。例如,某些存储引擎的某种索引,可能对一些特定的查询有优化
---《高性能MySQL》
五、执行器
MySQL 通过分析器知道了你要做什么,通过优化器知道了该怎么做,于是就进入了执行器阶段,开始执行语句。
开始执行的时候,要先判断一下你对这个表 T 有没有执行查询的权限,如果没有,就会返回没有权限的错误,在工程实现上,如果命中查询缓存,会在查询缓存返回结果的时候,做权限验证。查询也会在优化器之前调用 precheck 验证权限。
如果有权限,就打开表继续执行。
打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。比如我们这个例子中的表 T 中,id 字段没有索引,那么执行器的执行流程是这样的:
1.调用 InnoDB 引擎接口取这个表的第一行,判断 ID 值是不是 10,如果不是则跳过;
2.如果是则将这行存在结果集中;
3.调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。
执行器将上述遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。至此,这个语句就执行完成了。