手记

【数据库】了解MySQL中SQL语句的执行过程

写在前面的话

数据库对于Java后端开发人员是非常重要的一部分内容,MySQL作为开源免费的数据库管理系统,被广泛运用于Web领域,下面来介绍一下MySQL从启动到执行一条SQL语句的大致过程


首先从整体上来看:

MySQL采用客户端到服务器端的架构,分为服务器端和客户端,它们都是计算机的进程,一般采用TCP/IP作为它们通信的网络协议。

如果对进程,TCP/IP,计算机网络的应用层不太了解,可以参见一下博文:
【计算机网络】谢希仁笔记 运输层
【计算机网络】谢希仁笔记 网络层
【计算机网络】谢希仁笔记 应用层
如果MySQL客户端和服务器端不在同一台主机上,会采用TCP/IP协议
在同一台主机上的话,不同操作系统的通讯方式有所不同

我们输入用户名和密码 登陆M客户端之后,向服务器发送请求,服务器收到请求之后,对数据进行处理,然后把处理的结果返还给客户端。那么具体的情况是怎么样子的呢?下面让我好好说说。

1.MySQL服务器端启动

MySQL服务器启动的时候,会默认申请3306端口,之后监听这个端口号,等待客户端进程进行连接。


2.MySQL客户端启动

MySQL客户端启动后,会像操作系统申请端口号,然后通过TCP/IP协议与MySQL服务器进行连接,建立连接还要要与MySQL服务器层的连接器打交道,(1)连接器 负责跟客户端建立连接、获取权限、维持和管理连接。
当你的用户名和密码都正确,连接器会到权限表里面查出你拥有的权限。之后,这个连接里面的权限判断逻辑,都将依赖于此时读到的权限。

这里有一部分需要注意:一个用户成功建立连接后,即使你用管理员账号对这个用户的权限做了修改,也不会影响已经存在连接的权限。修改完成后,只有再新建的连接才会使用新的权限设置。

连接完成后,如果你没有后续的动作,这个连接就处于空闲状态,如果太长时间没动静,连接器就会自动将它断开。 这个时间是由参数wait_timeout控制的,默认值是8小时。

如果在连接被断开之后,客户端再次发送请求的话,就会收到一个错误提醒: Lost connection to MySQL server during query。这时候如果你要继续,就需要重连,然后再执行请求了。

建立连接的过程通常是比较复杂的,所以在使用中要尽量减少建立连接的动作,也就是尽量使用长连接。 但是全部使用长连接后,有些时候MySQL占用内存涨得特别快,这是因为MySQL在执行过程中临时使用的内存是管理在连接对象里面的。这些资源会在连接断开的时候才释放。所以如果长连接累积下来,可能导致内存占用太大,被系统强行杀掉(OOM),从现象看就是MySQL异常重启了。

解决这个问题有以下两种方案。

  1. 定期断开长连接。使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询再重连。
  2. 如果你用的是MySQL 5.7或更新版本,可以在每次执行一个比较大的操作后,通过执行 mysql_reset_connection来重新初始化连接资源。这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态。

3.MySQL客户端向MySQL服务器发送SQL语句处理的请求

在建立连接之后,我们就可以在客户端中输入一些命令程序作为请求发送给服务器的程序,服务器程序收到请求后会进行处理。

4.MySQL服务器处理SQL命令

这里就需要好好聊一聊MySQL服务器端的Server层和存储引擎层

Server层包括连接器查询缓存分析器优化器执行器等,涵盖MySQL的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。

存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持InnoDB、MyISAM、Memory等多个存储引擎。现在最常用的存储引擎是InnoDB,它从MySQL 5.5.5版本开始成为了默认存储引擎。

写在前面的话

数据库对于Java后端开发人员是非常重要的一部分内容,MySQL作为开源免费的数据库管理系统,被广泛运用于Web领域,下面来介绍一下MySQL从启动到执行一条SQL语句的大致过程


首先从整体上来看:

MySQL采用客户端到服务器端的架构,分为服务器端和客户端,它们都是计算机的进程,一般采用TCP/IP作为它们通信的网络协议。

如果对进程,TCP/IP,计算机网络的应用层不太了解,可以参见一下博文:
【计算机网络】谢希仁笔记 运输层
【计算机网络】谢希仁笔记 网络层
【计算机网络】谢希仁笔记 应用层
如果MySQL客户端和服务器端不在同一台主机上,会采用TCP/IP协议
在同一台主机上的话,不同操作系统的通讯方式有所不同

我们输入用户名和密码 登陆M客户端之后,向服务器发送请求,服务器收到请求之后,对数据进行处理,然后把处理的结果返还给客户端。那么具体的情况是怎么样子的呢?下面让我好好说说。

1.MySQL服务器端启动

MySQL服务器启动的时候,会默认申请3306端口,之后监听这个端口号,等待客户端进程进行连接。


2.MySQL客户端启动

MySQL客户端启动后,会像操作系统申请端口号,然后通过TCP/IP协议与MySQL服务器进行连接,建立连接还要要与MySQL服务器层的连接器打交道,(1)连接器 负责跟客户端建立连接、获取权限、维持和管理连接。
当你的用户名和密码都正确,连接器会到权限表里面查出你拥有的权限。之后,这个连接里面的权限判断逻辑,都将依赖于此时读到的权限。

这里有一部分需要注意:一个用户成功建立连接后,即使你用管理员账号对这个用户的权限做了修改,也不会影响已经存在连接的权限。修改完成后,只有再新建的连接才会使用新的权限设置。

连接完成后,如果你没有后续的动作,这个连接就处于空闲状态,如果太长时间没动静,连接器就会自动将它断开。 这个时间是由参数wait_timeout控制的,默认值是8小时。

如果在连接被断开之后,客户端再次发送请求的话,就会收到一个错误提醒: Lost connection to MySQL server during query。这时候如果你要继续,就需要重连,然后再执行请求了。

建立连接的过程通常是比较复杂的,所以在使用中要尽量减少建立连接的动作,也就是尽量使用长连接。 但是全部使用长连接后,有些时候MySQL占用内存涨得特别快,这是因为MySQL在执行过程中临时使用的内存是管理在连接对象里面的。这些资源会在连接断开的时候才释放。所以如果长连接累积下来,可能导致内存占用太大,被系统强行杀掉(OOM),从现象看就是MySQL异常重启了。

解决这个问题有以下两种方案。

  1. 定期断开长连接。使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询再重连。
  2. 如果你用的是MySQL 5.7或更新版本,可以在每次执行一个比较大的操作后,通过执行 mysql_reset_connection来重新初始化连接资源。这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态。

3.MySQL客户端向MySQL服务器发送SQL语句处理的请求

在建立连接之后,我们就可以在客户端中输入一些命令程序作为请求发送给服务器的程序,服务器程序收到请求后会进行处理。

4.MySQL服务器处理SQL命令

这里就需要好好聊一聊MySQL服务器端的Server层和存储引擎层

Server层包括连接器查询缓存分析器优化器执行器等,涵盖MySQL的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。

存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持InnoDB、MyISAM、Memory等多个存储引擎。现在最常用的存储引擎是InnoDB,它从MySQL 5.5.5版本开始成为了默认存储引擎。

对于连接器,我们在步骤2中已经介绍过了。

下面说说 (2)查询缓存

MySQL服务器程序处理查询请求的过程也是这样,先把刚刚处理过的查询请求和结果缓存起来,如果下一次有一模一样的请求过来,直接从缓存中查找结果。

这个查询缓存可以在不同客户端之间共享,但是如果两个查询请求在任何字符上的不同(例如:空格、注释、大小写),都会导致缓存不会命中。

当然也不是所有的查询请求与都会进行缓存,如果查询请求中包含某些系统函数、用户自定义变量和函数、一些系统表,如 mysql 、information_schema、performance_schema 数据库中的表,那这个请求就不会被缓存。

既然是缓存,那就有它缓存失效的时候。MySQL的缓存系统会监测涉及到的每张表,只要该表的结构或者数据被修改,那使用该表的所有高速缓存查询都将变为无效并从高速缓存中删除。

因为查询缓存的失效非常频繁,而且命中率是不高的,还需要耗费一定的资源去管理它。又被淘汰的趋势,比如MySQL 8.0版本直接将查询缓存的整块功能删掉了。


(3)分析器
如果没有命中查询缓存,就要开始真正执行语句了(MySQL8.0版本直接就进行这一步)

因为客户端程序发送过来的请求只是一段文本而已,所以MySQL服务器程序首先要对这段文本做分析,判断请求的语法是否正确,然后从文本中将要查询的表、各种查询条件都提取出来放到MySQL服务器内部使用的一些数据结构上来。

补充一个内容:Binlog是MySQL Server 有四种类型的日志之一,它包含了一些事件,这些事件描述了数据库的改动。


(4)优化器

语法分析之后,服务器程序获得到了需要的信息,比如要查询的列,表,搜索条件等等,我们写的MySQL语句执行起来效率可能并不是很高,MySQL的优化程序会对我们的语句做一些优化,如外连接转换为内连接、表达式简化、子查询转为连接等。

优化的结果就是生成一个执行计划,这个执行计划表明了应该使用哪些索引进行查询,表之间的连接顺序是什么样的。


(5)执行器
进入了执行器阶段,开始执行语句

开始执行的时候,要先判断一下你对这个表T有没有执行查询的权限,如果没有,就会返回没有权限的错误
如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的 (6)存储引擎 定义,去使用这个存储引擎提供的接口。

执行结束后,服务器获取结果

6.MySQL服务器向MySQL客户端返回执行结果

MySQL客户端接收结果。

这就是大致过程了,当然还有后面断开连接TCP/IP四次挥手什么的,这都是计算机网络的范畴了。

对于连接器,我们在步骤2中已经介绍过了。

下面说说 (2)查询缓存

MySQL服务器程序处理查询请求的过程也是这样,先把刚刚处理过的查询请求和结果缓存起来,如果下一次有一模一样的请求过来,直接从缓存中查找结果。

这个查询缓存可以在不同客户端之间共享,但是如果两个查询请求在任何字符上的不同(例如:空格、注释、大小写),都会导致缓存不会命中。

当然也不是所有的查询请求与都会进行缓存,如果查询请求中包含某些系统函数、用户自定义变量和函数、一些系统表,如 mysql 、information_schema、performance_schema 数据库中的表,那这个请求就不会被缓存。

既然是缓存,那就有它缓存失效的时候。MySQL的缓存系统会监测涉及到的每张表,只要该表的结构或者数据被修改,那使用该表的所有高速缓存查询都将变为无效并从高速缓存中删除。

因为查询缓存的失效非常频繁,而且命中率是不高的,还需要耗费一定的资源去管理它。又被淘汰的趋势,比如MySQL 8.0版本直接将查询缓存的整块功能删掉了。


(3)分析器
如果没有命中查询缓存,就要开始真正执行语句了(MySQL8.0版本直接就进行这一步)

因为客户端程序发送过来的请求只是一段文本而已,所以MySQL服务器程序首先要对这段文本做分析,判断请求的语法是否正确,然后从文本中将要查询的表、各种查询条件都提取出来放到MySQL服务器内部使用的一些数据结构上来。

补充一个内容:Binlog是MySQL Server 有四种类型的日志之一,它包含了一些事件,这些事件描述了数据库的改动。


(4)优化器

语法分析之后,服务器程序获得到了需要的信息,比如要查询的列,表,搜索条件等等,我们写的MySQL语句执行起来效率可能并不是很高,MySQL的优化程序会对我们的语句做一些优化,如外连接转换为内连接、表达式简化、子查询转为连接等。

优化的结果就是生成一个执行计划,这个执行计划表明了应该使用哪些索引进行查询,表之间的连接顺序是什么样的。


(5)执行器
进入了执行器阶段,开始执行语句

开始执行的时候,要先判断一下你对这个表T有没有执行查询的权限,如果没有,就会返回没有权限的错误
如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的 (6)存储引擎 定义,去使用这个存储引擎提供的接口。

执行结束后,服务器获取结果

6.MySQL服务器向MySQL客户端返回执行结果

MySQL客户端接收结果。

这就是大致过程了,当然还有后面断开连接TCP/IP四次挥手什么的,这都是计算机网络的范畴了。

0人推荐
随时随地看视频
慕课网APP