MySQL

您所在的位置:网站首页 mysql语句执行流程 MySQL

MySQL

2023-08-22 05:17| 来源: 网络整理| 查看: 265

文章目录 一条语句执行流程与原理分析执行流程原理分析1.客户端/服务端建立连接2.客户端/服务端通信协议2.查询缓存3.语法解析和预处理(做什么)4.查询优化(怎么做)5.查询执行引擎6.返回结果

一条语句执行流程与原理分析

img

执行流程

客户端向MySQL服务器发送一条查询请求

服务器首先先检查查询缓存,如果命中缓存,则立刻返回存储在缓存中的结果。否则进入下一阶段

服务器进行SQL解析、预处理、再由优化器生成对应的执行计划

MySQL根据执行计划,调用存储引擎的API来执行查询

将结果返回给客户端,同时缓存查询结果

原理分析 1.客户端/服务端建立连接

在通信之前,首先要建立连接。

一般在同一台计算机上的话,可以采用进程间的通信方式,比如:windows下命名管道和共享内存、linux下可以使用套接字,其实大致原理是相似的:

命名管道:服务器创建命名管道,能接受管道客户机的连接请求。而客户机同一个现成的命名管道服务器建立连接。

linux下可以使用套接字大致流程:

1、服务器端

首先服务器应用程序用系统调用socket来创建一个套接字,它是系统分配给该服务器进程的类似文件描述符的资源,它不能与其他的进程共享。

接下来,服务器进程会给套接字起个名字,我们使用系统调用bind来给套接字命名。然后服务器进程就开始等待客户连接到这个套接字。

然后,系统调用listen来创建一个队列并将其用于存放来自客户的进入连接。

最后,服务器通过系统调用accept来接受客户的连接。它会创建一个与原有的命名套接不同的新套接字,这个套接字只用于与这个特定客户端进行通信,而命名套接字(即原先的套接字)则被保留下来继续处理来自其他客户的连接。

2、客户端

基于socket的客户端比服务器端简单,同样,客户应用程序首先调用socket来创建一个未命名的套接字,然后将服务器的命名套接字作为一个地址来调用connect与服务器建立连接。

一旦连接建立,我们就可以像使用底层的文件描述符那样用套接字来实现双向数据的通信。

但常常客户端和服务器不是在一台计算机上通信,所以使用最多的方式是TCP/IP方式。通过三次握手建立连接。 在这里插入图片描述

2.客户端/服务端通信协议

MySQL客户端/服务端通信协议是“半双工”的:在任意时刻,要么是服务器向客户端发送数据,要么是客户端向服务器发送数据,这两个动作不能同时发生。一旦一端开始发送消息,另一端要接受完整个消息才能响应它,所以我们无法也无须将一个消息切成小块独立发送,也没有办法进行流量控制。

根据串行数据的传输方向,将通信分为单工、半双工和全双工:

单工:是指数据传输仅能沿一个方向,不能实现反向传输。

半双工:是指数据传输可以沿两个方向,但需要分时进行传输。

全双工:是指数据可以同时进行双向传输。

img

客户端用一个单独的数据包将查询请求发送给服务器,所以当查询语句很长的时候,需要设置max_allowed_packet参数。但是需要的注意的是,如果查询实在是太大,服务端会拒绝接受更多数据并抛出异常。

与之相反的是,服务器响应给用户的数据通常会很多,由多个数据包组成。但是当服务器响应客户端请求时,客户端必须完整的接受整个返回结果,而不能简单的只取前面几条结果,然后让服务器停止发送。因而在实际开发中,尽量保持查询简单且只返回必需的数据,减小通信间数据包的大小和数量是一个非常好的习惯,这也是查询中尽量避免使用SELECT * 以及加上LIMIT限制的原因之一。

2.查询缓存

在解析一个查询语句前,如果查询缓存是打开的,那么MySQL会检查这个查询语句是否命中查询缓存中的数据。如果当前查询恰好命中查询缓存,在检查一次用户权限后直接返回缓存中的结果。这种情况下,查询不会被解析,也不会生成执行计划,更不会执行。

MySQL将缓存存放在一个引用表(类似于HashMap的数据结构),通过一个哈希值索引,这个哈希值通过查询本身、当前要查询的数据库、客户端协议版本号等一些可能影响结果的信息计算得来。所以两个查询在任何字符上的不同(空格、注释),都会导致缓存不会命中。

如果查询中包含任何用户自定义函数、存储函数、用户变量、临时表、mysql库中的系统表,其查询结果都不会被缓存。比如函数NOW()或者CURRENT_DATE()会因为不同的查询时间,返回不同的查询结果,再比如包含CURRENT_USER或者CONNECION_ID()的查询语句会因为不同的用户而返回不同的结果,将这样的查询结果缓存起来没有任何的意义。

缓存失效

MySQL的查询缓存系统会跟踪查询中涉及的每个表,如果这些表(数据或结构)发生变化,那么和这张表相关的所有缓存数据都将失效。

正因为如此,在任何的写操作时,MySQL必须将对应表的所有缓存都设置为失效。如果查询缓存非常大或者碎片很多,这个操作就可能带来很大的系统消耗,甚至导致系统僵死一会儿。而且查询缓存对系统的额外消耗也不仅仅在写操作,读操作也不例外:

1.任何的查询语句在开始之前都必须经过检查,即使这条SQL语句永远不会命中缓存

2.如果查询结果可以被缓存,那么执行完成后,会将结果存入缓存,也会带来额外的系统消耗

所以,mysql8.0,退出了对查询缓存的支持。

3.语法解析和预处理(做什么)

MySQL通过关键字将SQL语句进行解析,并生成一颗对应的解析树。这个过程解析器主要通过语法规则来验证和解析。比如SQL中是否使用了错误的关键字或者关键字的顺序是否正确等等。预处理则会根据MySQL规则进一步检查解析树是否合法。比如检查要查询的数据表和数据列是否存在等等。

本质上是语法分析、语义分析

4.查询优化(怎么做)

经过了分析器后,MySQL就知道你要做什么了。但是怎么做才更加高效呢,这就是优化器要做的事了。优化器会考虑这个表中有哪些索引呢,应该使用哪个索引呢。多表联查的时候,要决定各个表的顺序。

语法树被认为是合法之后,并且有优化器将其转化成查询计划,多数情况下,一条查询可以有很多种执行方式,最后都返回相应的结果,优化器的作用就是找到这其中最好的执行计划。

MySQL使用基于成本的优化器,它尝试预测一个查询使用某种执行计划时的成本,并选择其中成本最小的一个。这个成本是根据一些列的统计信息计算得来的,这些统计信息包括:每张表或者索引的页面个数、索引的基数、索引和数据行的长度、索引的分布情况等等。(成本小并不意味着执行时间短。)

成本一般分为两部分:IO成本和CPU成本

我们的表经常使用的MyISAM、InnoDB存储引擎都是将数据和索引都存储到磁盘上的,当我们想查询表中的记录时,需要先把数据或者索引加载到内存中然后再操作。这个从磁盘到内存这个加载的过程损耗的时间称之为I/O成本。

读取以及检测记录是否满足对应的搜索条件、对结果集进行排序等这些操作损耗的时间称之为CPU成本。

MySQL的查询优化器是一个非常复杂的部件,它使用了非常多的优化策略来生成一个最优的执行计划:

重新定义表的关联顺序(多张表关联查询时,并不一定按照SQL中指定的顺序进行,但有一些技巧可以指定关联顺序)优化MIN()和MAX()函数(找某列的最小值,如果该列有索引,只需要查找B+Tree索引最左端,反之则可以找到最大值)提前终止查询(使用Limit时,查找到满足数量的结果集后会立即终止查询)… 5.查询执行引擎

在完成解析和优化阶段以后,MySQL会生成对应的执行计划,查询执行引擎根据执行计划给出的指令逐步执行得出结果。整个执行过程的大部分操作均是通过调用存储引擎实现的接口来完成,这些接口被称为handler API。查询过程中的每一张表由一个handler实例表示,实际上,MySQL在查询优化阶段就为每一张表创建了一个handler实例,优化器可以根据这些实例的接口来获取表的相关信息,包括表的所有列名、索引统计信息等。存储引擎接口提供了非常丰富的功能,但其底层仅有几十个接口,这些接口像塔积木一样完成了一次查询的大部分操作。

6.返回结果

查询执行的最后一个阶段就是将结果返回给客户端。即使查询不到数据,MySQL仍然会返回这个查询的相关信息,比如该查询影响到的行数以及执行时间等等。

如果查询缓存被打开且这个查询可以被缓存,MySQL也会将结果存放到缓存中。

结果集返回客户端是一个增量且逐步返回的过程。有可能MySQL在生成第一条结果时,就开始向客户端逐步返回结果集了。这样服务端就无须存储太多结果而消耗过多内存,也可以让客户端第一时间获得返回结果。

参考链接:MySQL原理

参考链接:通信协议分类

Linux进程间通信——套接字



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3