大家好!这是我的第一篇文章。
这篇文章我将介绍一条sql查询语句是如何执行的
总的来说,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 5.7 或更高版本,您可以在执行资源密集型操作后使用 mysql_reset_connection 来重新初始化连接资源。此过程不需要重新连接或重新验证,而是将连接重置为刚刚创建的状态。
查询缓存
注意:从 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 列上没有索引。执行者的执行过程如下:
- 调用innodb引擎接口取出表的第一行,检查id值是否为10,如果不是则跳过;如果是,则将其添加到结果集中。
- 调用引擎接口获取“下一行”,重复相同的逻辑,直到检查完所有行。
- 执行器将累积的结果集返回给客户端。
至此,查询完成。
对于索引表,该过程涉及使用引擎的预定义方法来迭代获取“第一个匹配行”和“下一个匹配行”。
慢查询日志中,rows_examined字段表示查询执行过程中扫描的行数。每次执行器调用引擎检索数据行时,该值都会累加。
在某些情况下,对执行器的单次调用可能涉及在引擎内部扫描多行。因此,引擎扫描的行数不一定等于 rows_examined.
结尾
感谢您的阅读!希望文章对您有所帮助。