做为一名 Java开发人员,写 SQL 语句是常有的事,可是你知道 SQL 语句背后的处理逻辑吗?好比下面这条 SQL 语句:java
select * from user where id=1
复制代码
执行完这条语句后,咱们就会获得 id 为 1 的用户信息。那么对于这一条 SQL 语句,MySQL服务器作了哪些处理呢?这篇文章咱们就一块儿打卡 MySQL 数据库中对 SQL 语句的处理逻辑。mysql
了解 MySQL 数据库的 SQL 语句内部处理逻辑有什么好处?当咱们碰到 MySQL 的一些异常或者问题时,就可以直戳本质,更为快速地定位并解决问题。算法
想要更好的了解 SQL 语句的内部处理逻辑,咱们能够先看 MySQL 的基本架构图,这样咱们能够站在更高的角度去俯瞰 MySQL 数据库,MySQL 的基本架构示意图以下:sql
从图中,咱们能够清晰的看出 MySQL 的架构和各个模块以及 SQL 语句的执行过程,MySQL 数据库总体能够分为 Server 层和存储引擎层两部分,其中 Server 层是共有的,而存储引擎层则是能够以插件的形式进行扩展。一条 SQL 语句大概会经历连接管理、解析与优化、最后到存储引擎,这三个模块。接下来咱们就来聊一聊这三个模块。数据库
链接管理是 SQL 语句执行过程当中碰到的第一关,连接管理就像一扇大门同样,控制着客户端与 Server 服务端的交互,链接管理主要工做是客户端的身份认证和链接线程的管理。缓存
每一个客户端与 Server 创建链接时,服务端都会建立一个线程来与客户端进行交互,交互的第一项内容就是验证客户端的身份,认证凭据是基于客户端发起链接请求时携带的主机信息、用户名、密码。若是认证失败,则结束链接任务,而且返回的 Access denied for user
错误。bash
若是认证成功,链接管理还会作一件事情,到权限表中查询出该用户的权限,在此次链接下,后续的权限判断都是基于此时读取的权限为依据,也就是说链接成功后,即便管理员对这个用户作了权限修改,也不会影响此次链接的权限验证。服务器
链接管理须要作的事情就比较简单,主要是负责客户端与服务端进行链接,固然在链接线程上,链接管理也作了优化,并非每一个客户端执行完任务以后,就把该线程销毁,链接管理会把这些线程缓存起来,等待新的链接,这也就不会频繁的建立和销毁线程,从而节约了开销。微信
完成链接管理以后,SQL 语句执行的第二步就是解析和优化,这一步就很是的复杂,SQL 语句查询的全部操做都在这里了。咱们能够将这一步细分为 4 小步。架构
在 MySQL 服务端也有缓存,这是一个很是鸡肋的功能,为何呢?看完了你就知道了。
MySQL 服务器拿到查询请求后,会先到查询缓存看看,以前是否是执行过这条语句。以前执行过的语句及其结果可能会以 key-value 对的形式,被直接缓存在内存中。key 是查询的语句,value 是查询的结果。若是你的查询可以直接在这个缓存中找到 key,那么这个 value 就会被直接返回给客户端。若是语句不在查询缓存中,就会继续后面的执行阶段。执行完成后,执行结果会被存入查询缓存中。
看上去没毛病,这样作会大大提高 MySQL 的性能,然而,你想多了,MySQL 的查询缓存命中率很是的低,主要缘由是若是两个查询请求在任何字符上的不一样(例如:空格、注释、大小写),都会致使缓存不会命中。
还有就是缓存有可能获取到错误的数据,以某些系统函数举例,可能一样的函数的两次调用会产生不同的结果,好比函数NOW,每次调用都会产生最新的当前时间,若是在一个查询请求中调用了这个函数,那即便查询请求的文本信息都同样,那不一样时间的两次查询也应该获得不一样的结果,若是在第一次查询时就缓存了,那第二次查询的时候直接使用第一次查询的结果就是错误的!
除了这些以外,MySQL 缓存的失效也很是的频繁,MySQL的缓存系统会监测涉及到的每张表,只要该表的结构或者数据被修改,如对该表使用了 INSERT、 UPDATE、DELETE、TRUNCATE TABLE、ALTER TABLE、DROP TABLE 或 DROP DATABASE 语句,那使用该表的全部高速缓存查询都将变为无效并从高速缓存中删除!
看到这里你知道查询缓存很鸡肋了吧,缓存对 MySQL 数据库来讲弊大于利,因此在 MySQL 8.0 版本直接将查询缓存的整块功能删掉了
若是查询缓存没有命中,接下来就须要进入正式的查询阶段了。由于客户端程序发送过来的请求只是一段文本而已,因此 MySQL 服务器程序首先要对这段文本作语法解析。
首先经过关键字将 SQL 语句进行解析,而且生成一个“解析树”。MySQL 解析器将使用 MySQL 语法规则验证和解析查询,例如,关键字是否使用正确、关键字的顺序是否正确或者引号是否先后匹配等。
预处理是根据一些 MySQL 规则进一步检查解析树是否合法,例如数据表和数据列是否存在,还会解析名字和别名,看看他们是否有歧义等。
语法解析和预处理以后,你的需求就明白了,须要查询哪张表,查询的数据列是哪些、条件是什么等等。可是使用怎么样的方式是最优查询方式呢?查询优化就是来干这个事的,MySQL 的优化程序会对咱们的语句作一些优化,如外链接转换为内链接、表达式简化、子查询转为链接等等。优化的结果就是生成一个执行计划,这个执行计划代表了应该使用哪些索引进行查询,表之间的链接顺序是啥样的。
执行器会执行查询优化后的执行计划,经过与存储引擎交互,完成数据的查询操做,返回最终的数据结果。
开始执行的时候,要先判断一下你对这个表 T 有没有执行查询的权限,若是没有,就会返回没有权限的错误,以下所示 (在工程实现上,若是命中查询缓存,会在查询缓存返回结果的时候,作权限验证。查询也会在优化器以前调用 precheck 验证权限)。
mysql> select * from user where ID=1;
ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'T'
复制代码
若是有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。
好比咱们这个例子中的表 user 中,假设 ID 字段没有索引,那么执行器的执行流程是这样的:
一、调用 InnoDB 引擎接口取这个表的第一行,判断 ID 值是否是 10,若是不是则跳过,若是是则将这行存在结果集中;
二、调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。
三、执行器将上述遍历过程当中全部知足条件的行组成的记录集做为结果集返回给客户端。
到这里,执行 SQL 语句就执行完了,其实这内部仍是很是复杂的。
到上面为止,SQL 语句就执行完了,可是与真实数据打交道的是存储引擎,存储引擎是 MySQL服务器对数据的存储和提取操做的封装模块。咱们知道表是由一行一行的记录组成的,但这只是一个逻辑上的概念,物理上如何表示记录,怎么从表中读取数据,怎么把数据写入具体的物理存储器上,这都是存储引擎负责的事情。
为了实现不一样的功能,MySQL提供了各式各样的存储引擎,不一样存储引擎管理的表具体的存储结构可能不一样,采用的存取算法也可能不一样。好比,MySQL5.7 以后默认的 InnoDB 存储引擎。
能够看出一条 SQL 语句的执行仍是很是复杂的,涉及到了不少的模块,文章到这里就结束了,感谢您的阅读,但愿这篇文章对你的学习和工做有所帮助,若是您以为文章有用,欢迎点赞+转发。
目前互联网上不少大佬都有 MySQL 内部架构相关文章,若有雷同,请多多包涵了。原创不易,码字不易,还但愿你们多多支持。若文中有所错误之处,还望提出,谢谢。
欢迎扫码关注微信公众号:「平头哥的技术博文」,和平头哥一块儿学习,一块儿进步。