SQL 查询语句是如何执行的
大家好!这是我的第一篇文章。
在本文中,我将介绍如何执行一条sql查询语句
下面是MySQL架构图:

总体来说,MySQl可以分为两部分:服务器层和存储引擎层。
服务器层包括连接器、查询缓存、解析器、优化器、执行器等,包含 MySQL 的大部分核心服务功能以及所有内置函数(例如日期、时间、数学和加密函数)。所有跨存储引擎的功能(例如存储过程、触发器和视图)都在此层实现。
存储引擎层负责数据的存储和检索。其架构为插件式,支持InnoDB、MyISAM、Memory等多种存储引擎。从MySQL 5.5.5开始,InnoDB成为MySQL的默认存储引擎。
您可以在创建表时通过使用带有“engine=memory”的“create table”语句来指定内存引擎。
**不同的存储引擎共享同一个Server层**
连接器
第一步是连接数据库,需要用到连接器,连接器负责与客户端建立连接,获取权限以及维护和管理连接,连接命令为:
mysql -h$ip -P$port -u$user -p
该命令用于与服务器建立连接,完成经典的TCP握手后,连接器会使用提供商的用户名和密码来验证您的身份。
这意味着一旦成功建立连接,管理员对用户权限所做的任何更改都不会影响现有连接的权限。只有新连接才会使用更新后的权限设置。
连接建立之后,若没有后续动作,连接将进入空闲状态,可以使用`show processlist`命令查看:

如果客户端长时间不活动,连接器将自动断开连接。持续时间由 `wait_timeout` 参数控制,默认为 8 小时。
如果连接终止,客户端发送请求,则会收到错误消息:“查询期间与 MySQL 服务器失去连接”。要继续,您需要重新连接,然后执行请求。
在数据库中,**长连接**是指客户端在成功连接后,对连续的请求保持同一个连接。短连接是指在进行几次查询后断开连接,并在后续查询时重新连接。
由于连接过程很复杂,建议在开发过程中尽量减少连接的创建,即尽可能使用持久连接。
但是使用长连接时,MySQL 的内存占用可能会大幅增加,因为执行过程中使用的临时内存是在连接对象内部管理的,只有当连接终止时才会释放这些资源。如果长连接不断积累,可能会导致内存占用过大,从而导致系统强制终止 MySQL(OOM),从而导致意外重启。
**解决方案**:
查询缓存
注意:从 MySQL 8.0 开始,查询缓存功能已被完全删除,因为**它的缺点大于它的优点**。
当 MySQL 收到一个查询请求时,它首先会检查查询缓存,看这个查询是否之前被执行过。之前执行过的查询和它们的结果会以键值对的形式缓存在内存中。键是查询语句,值是结果。如果在查询缓存中找到该键,则直接将值返回给客户端。
如果在查询缓存中找不到该查询,则该过程继续。
**为什么查询缓存弊大于利?**
查询缓存失效现象发生得非常频繁,任何对表的更新都会清除与该表相关的所有查询缓存,导致缓存命中率非常低,除非该表是静态配置表。
MySQL 提供了一种“按需”使用查询缓存的方法。通过将参数“query_cache_type”设置为 DEMAND,SQL 语句将默认不使用查询缓存。要使用查询缓存,可以显式指定“SQL_CACHE”:
select SQL_CACHE * from T where ID=10;
解析器
如果查询缓存未命中,则开始语句执行过程。MySQL 首先需要了解要做什么,因此它会解析 SQL 语句。
解析器首先进行词法分析,输入的 SQL 语句由字符串和空格组成,MySQL 会对其进行分析,识别出各个部分代表什么,例如 `select` 被识别为查询语句,`T` 被识别为 `表名`,`ID` 被识别为 `列`。
词法分析之后,接着进行语法分析,语法分析器根据词法分析的结果,判断SQL语句是否符合MySQL的语法规则。
如果有语法错误,则会显示类似“您的 SQL 语法有错误”的错误消息。例如,在以下查询中,select 关键字拼写错误:
mysql> elect * from t 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 t where ID=1' at line 1
优化器
解析后,MySQL 知道您要做什么。接下来,优化器决定如何做。
当一个表有多个索引时,优化器决定使用哪个索引;当一个查询涉及多个表时,优化器决定表连接的顺序。例如,在以下查询中:
select * from t1 join t2 using(ID) where t1.c=10 and t2.d=20;
查询可以从 t1 或 t2 中检索值开始。两种方法都会产生相同的逻辑结果,但它们的性能可能不同。优化器的作用是选择最有效的计划。
优化阶段结束后,流程进入执行器。
执行者
执行器开始执行查询。
执行前先检查当前连接是否有权限查询该表,如果没有权限则返回权限不足的错误(从查询缓存返回结果时也会进行权限检查)。
如果授予权限,则打开表并继续执行。在此过程中,执行器根据表的引擎定义与存储引擎进行交互。
例如,假设表T在ID列上没有索引,执行器的执行流程如下:
至此,查询完成。
对于索引表,该过程涉及使用引擎的预定义方法迭代地获取“第一个匹配的行”和“下一个匹配的行”。
在慢查询日志中,rows_examined 字段表示查询执行过程中扫描的行数。每次执行器调用引擎检索数据行时,该值都会累积。
在某些情况下,对执行器的一次调用可能涉及在引擎内部扫描多行。因此,**引擎扫描的行数不一定等于 rows_examined**。
结尾
感谢您的阅读!希望文章能够对您有所帮助。