对于一个作后台不久的我,起初作项目只是实现了功能,所谓的增删改查,和基本查询索引的创建。直到有一个面试官问我一个问题,一条sql查询语句在mysql数据库中具体是怎么执行的?我被虐了,很开心,感谢他。因而开始了深刻学习mysql。本篇文章经过mysql
一条sql查询语句在mysql数据库中具体是怎么执行的?程序员
来具体讲解mysql的基础架构。面试
mysql> select * from Student where ID=1;
复制代码
上面一条简单的查询语句很简单,但我想好多开发者并不知道在MYSQL内部的执行过程。sql
Mysql基本架构示意图。 数据库
Server层包括链接器、查询缓存、分析器、优化器、执行器等,这些涵盖了MySQL的大多数核心服务和全部的内置函数(如日期、时间、数学和加密函数等),跨存储引擎的功能都在这一层实现,好比存储过程、触发器、视图等。缓存
存储引擎层负责数据的存储和提取,提供数据的读写接口。其架构模式是插件式的,支持InnoDB、MyISAM、Memory等多个存储引擎。目前开发中最经常使用的存储引擎是InnoDB,它从MySQL5.5.5版本开始成为默认存储引擎,不过开发者也能够经过指定存储引擎的类型来选择别的引擎。服务器
即便存储引擎不一样,可是也会共用一个Server层,接下来对Server层中的执行流程,依次对其做用进行讲解。微信
运行查询语句开始查询的前提是第一步先链接数据库,这时候等待你的就是链接器。链接器负责和客户端创建链接、获取权限、维持和管理链接。架构
常规的开发模式,客户端与服务器须要创建链接。两者在完成经典的TCP握手后,Server层链接器就要开始认证你的身份,这个时候是服务器端代码使用的用户名和密码。函数
链接器一些内容说明:
mysql>show processlist
复制代码
链接断开相关:客户端若是太长时间没动静,链接器就会自动将它断开。这个时间是由参数 wait_timeout 控制的,默认值是 8 小时。 若是在链接被断开以后,客户端再次发送请求的话,就会收到一个错误提醒: Lost connection to MySQL server during query。这时候若是你要继续,就须要重连,而后再执行请求了。数据库里面,长链接是指链接成功后,若是客户端持续有请求,则一直使用同一个链接。短链接则是指每次执行完不多的几回查询就断开链接,下次查询再从新创建一个。
创建链接的过程一般是比较复杂的,因此我建议你在使用中要尽可能减小创建链接的动做,也就是尽可能使用长链接。
较好的链接方式长链接产生的问题以及解决办法:
所有使用长链接后,你可能会发现,有些时候 MySQL 占用内存涨得特别快,这是由于 MySQL 在执行过程当中临时使用的内存是管理在链接对象里面的。这些资源会在链接断开的时候才释放。因此若是长链接累积下来,可能致使内存占用太大,被系统强行杀掉(OOM),从现象看就是 MySQL 异常重启了。怎么解决这个问题呢?你能够考虑如下两种方案。
按期断开长链接。使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开链接,以后要查询再重连。
若是你用的是 MySQL 5.7 或更新版本,能够在每次执行一个比较大的操做后,经过执行 mysql_reset_connection 来从新初始化链接资源。这个过程不须要重连和从新作权限验证,可是会将链接恢复到刚刚建立完时的状
第一步链接创建完成后,就能够执行查询语句了。第二部:查询缓存。 Mysql肯定了查询语句,会先到查询缓存中,看以前是否执行过这条查询语句。以前若是执行过这条查询语句,查询结果可能会以key-value的方式直接缓存在内存中。key是查询的语句,value是查询到的值,这样的话查询缓存会直接把value值返回给客户端。查询语句若是步子查询缓存中,会正常往下执行,获取到新的查询结果后会被存入到查询缓存中。
说明:
大多数状况下并不建议使用查询缓存。查询缓存每每弊大于利。
查询缓存的失效很是频繁,只要有对某个表的更新,该表的全部查询缓存都会被清空。因此极可能你费劲把结果存起来,尚未使用,就被一个更新所有清空了,尤为是对于更新压力大的数据库来讲,查询缓存的命中率很低。可是也不是不能使用,假如一张静态表(系统配置表),很长时间更新一次,这种状况就比较适合使用查询缓存。
如何设置Mysql不使用查询缓存
将Mysql参数query_cache_type设置成DEMAND,这样默认的SQL语句都不使用查询缓存
如何对某一条查询语句指定使用查询缓存 肯定使用查询缓存的语句,能够用SQL_CACHE显示指定
mysql> select SQL_CACHE * from Student where ID=1; 复制代码复制代码
注意:
Mysql 8.0版本直接将查询缓存对整块功能删除掉了,8.0以后将再也不出现查询缓存。
若是在查询缓存中未找到缓存数据,就会开始真正的执行查询语句。Mysql须要直到这条查询语句要作什么?所以须要对SQL语句作解析。
解析流程:
词法分析
分析器首先会作词法分析,查询语句中包括了多个字符串和空格组成,Mysql须要识别出里面的字符串分别表明什么。
mysql> select * from Student where ID=1;
复制代码
分析这条查询语句,"select"关键字能够识别出是一个查询语句。 字符串"Student"识别出是表名"Student",把字符串"ID"识别成列"ID"。
语法分析
词法分析后,语句法分析会根据语法规则,判断输入的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”的内容
分析器执行以后,到达了优化器。
优化器会作那些优化处理:
当在表中有多个索引的时候,优化器会决定这条查询语句使用哪一个索引
一个查询语句有多表关联(join)的时候,决定各个表的链接顺序。
例子以下:
mysql> select * from t1 join t2 using(ID) where t1.c=10 and t2.d=20;
复制代码
两种关联查询方案结果确定是同样的,可是执行效率会有不一样,优化器就是决定选择使用哪个方案。
MySQL 经过分析器知道了你要作什么,经过优化器知道了该怎么作(执行方案是什么?),因而就进入了执行器阶段,开始执行语句。
开始执行的时候,要先判断一下你对这个表 Student 有没有执行查询的权限,若是没有,就会返回没有权限的错误,以下所示 (在工程实现上,若是命中查询缓存,会在查询缓存返回结果的时候,作权限验证。查询也会在优化器以前调用 precheck 验证权限)。
mysql> select * from Student where ID=10; 复制代码ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'Student' 复制代码
若是有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。
来到存储引擎,执行存储引擎提供的数据读写接口。 这条查询语句,执行器(注意这里读写数据的仍是存储引擎)读写数据的流程要分两种状况考虑:
表 Student 中,ID字段没有索引,执行流程以下:
调用 InnoDB 引擎接口取这个表的第一行,判断 ID 值是否是 1,若是不是则跳过,若是是则将这行存在结果集中;
调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。
执行器将上述遍历过程当中全部知足条件的行组成的记录集做为结果集返回给客户端。
至此,这个语句就执行完成了。
表 Student 中,ID字段有索引,那么执行器的执行流程是这样的:
有索引的表,执行的逻辑也差很少。第一次调用的是“取知足条件的第一行”这个接口,以后循环取“知足条件的下一行”这个接口,这些接口都是引擎中已经定义好的。
到此,一条查询语句在mysql架构中执行基本流程进行了一个大概的讲解。在这个流程中,会有不少细节和可深挖学习的地方,例如关联(join)、索引、日志系统等,接下来会继续学习并记录一些MySql深刻的东西。
力推文章:
如何写优雅的SQL原生语句? 两篇文章一块儿学习能完全搞懂sql语句到底怎么在架构中执行的,到底应该怎么写优秀的sql。
以为本文对你有帮助?请分享给更多人
欢迎你们关注个人公众号——程序员成长指北。请自行微信搜索——“程序员成长指北”