提出问题:
对于一个做后台不久的我,起初做项目只是实现了功能,所谓的增删改查,和基本查询索引的建立。直到有一个面试官问我一个问题,一条sql查询语句在mysql数据库中具体是怎么执行的?我被虐了,很开心,感谢他。于是开始了深入学习mysql。本篇文章通过
一条sql查询语句在mysql数据库中具体是怎么执行的?
来具体讲解mysql的基础架构。
讲解
mysql> select * from Student where ID=1;
上面一条简单的查询语句很简单,但我想好多开发者并不知道在MYSQL内部的执行过程。
Mysql基本架构示意图
Mysql基本架构示意图。 从图中可以看出Mysql可以大体分为Server层和存储引擎层两部分。
Server层包括连接器、查询缓存、分析器、优化器、执行器等,这些涵盖了MySQL的大多数核心服务和所有的内置函数(如日期、时间、数学和加密函数等),跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。
存储引擎层负责数据的存储和提取,提供数据的读写接口。其架构模式是插件式的,支持InnoDB、MyISAM、Memory等多个存储引擎。目前开发中最常用的存储引擎是InnoDB,它从MySQL5.5.5版本开始成为默认存储引擎,不过开发者也可以通过指定存储引擎的类型来选择别的引擎。
即使存储引擎不同,但是也会共用一个Server层,接下来对Server层中的执行流程,依次对其作用进行讲解。
连接器
运行查询语句开始查询的前提是第一步先连接数据库,这时候等待你的就是连接器。连接器负责和客户端建立连接、获取权限、维持和管理连接。
常规的开发模式,客户端与服务器需要建立连接。二者在完成经典的TCP握手后,Server层连接器就要开始认证你的身份,这个时候是服务器端代码使用的用户名和密码。
连接器一些内容说明:
mysql>show processlist
较好的连接方式长连接产生的问题以及解决办法:
全部使用长连接后,你可能会发现,有些时候 MySQL 占用内存涨得特别快,这是因为 MySQL 在执行过程中临时使用的内存是管理在连接对象里面的。这些资源会在连接断开的时候才释放。所以如果长连接累积下来,可能导致内存占用太大,被系统强行杀掉(OOM),从现象看就是 MySQL 异常重启了。怎么解决这个问题呢?你可以考虑以下两种方案。
定期断开长连接。使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询再重连。
如果你用的是 MySQL 5.7 或更新版本,可以在每次执行一个比较大的操作后,通过执行 mysql_reset_connection 来重新初始化连接资源。这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状
查询缓存
第一步连接建立完成后,就可以执行查询语句了。第二部:查询缓存。 Mysql确定了查询语句,会先到查询缓存中,看之前是否执行过这条查询语句。之前如果执行过这条查询语句,查询结果可能会以key-value的方式直接缓存在内存中。key是查询的语句,value是查询到的值,这样的话查询缓存会直接把value值返回给客户端。查询语句如果步子查询缓存中,会正常往下执行,获取到新的查询结果后会被存入到查询缓存中。
说明:
ounter(line
ounter(line
注意:
Mysql 8.0版本直接将查询缓存对整块功能删除掉了,8.0之后将不再出现查询缓存。
分析器
如果在查询缓存中未找到缓存数据,就会开始真正的执行查询语句。Mysql需要直到这条查询语句要做什么?因此需要对SQL语句做解析。
解析流程:
ounter(line
语法分析
词法分析后,语句法分析会根据语法规则,判断输入的SQL语句是否满足MySql语法。如果语法不对,会收到“You have an error in your SQL syntax”的错误提醒。例如
mysql> elect * from Student 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
技巧:一般语法错误看错误提示的时候,要关注的是紧接“use near”的内容
ounter(line
ounter(line
ounter(line
优化器
分析器执行之后,到达了优化器。
优化器会做那些优化处理:
该例子既可以先从表t1里面取出c=10的记录的ID值,再根据ID值关联到表t2,再判断t2里面d的值是否等于20.
也可以先从表 t2 里面取出 d=20 的记录的 ID 值,再根据 ID 值关联到 t1,再判断 t1 里面 c 的值是否等于 10。
ounter(line
执行器
MySQL 通过分析器知道了你要做什么,通过优化器知道了该怎么做(执行方案是什么?),于是就进入了执行器阶段,开始执行语句。
开始执行的时候,要先判断一下你对这个表 Student 有没有执行查询的权限,如果没有,就会返回没有权限的错误,如下所示 (在工程实现上,如果命中查询缓存,会在查询缓存返回结果的时候,做权限验证。查询也会在优化器之前调用 precheck 验证权限)。
mysql> select * from Student where ID=10;
ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'Student'
如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。
存储引擎
来到存储引擎,执行存储引擎提供的数据读写接口。 这条查询语句,存储引擎读写数据的流程要分两种情况考虑:
总结
到此,一条查询语句在mysql架构中执行基本流程进行了一个大概的讲解。在这个流程中,会有很多细节和可深挖学习的地方,例如关联(join)、索引等,接下来会继续学习并记录一些MySql深入的东西。