Mysql 在命令行输入 SELECT 语句后,背后发生了什么事?

前言

当我们在 Mysql 的命令行里输入一条 SELECT 语句后,可能马上就会返回我们想要的数据,可能要等很久才返回数据,也可能执行的过程中报了一些错误。

一条简单的 SELECT 的语句背后到底发生了什么事,它的执行逻辑是怎样的?

我们来看看当输入一条 SELECT 语句后,Mysql 背后发生了一些什么事。

MySql 逻辑结构

Mysql 分为客户端服务端

我们平时使用的命令行工具是客户端,客户端把命令发送到服务端,服务端处理完后,把结果返回给客户端。

服务端是 Mysql 最核心的部分,所有的操作基本都在服务端完成。

MySql服务端

来自极客时间《MySQL实战45讲》

在服务端里又分为 Server 层存储引擎层部分。

Server 层里面包含了连接器、查询缓存、分析器、优化器、执行器等。

大多数的核心功能都在这一层,比如所有的内置函数,跨存储引擎的功能,比如存储过程 、触发器,视图等。

如图,Mysql 的每一次完整操作,都是按顺序的上到下执行。

客户端发起请求 -> 连接器建立连接 -> 查询缓存或者直接到分析器进行 SQL 语句分析 -> 优化器优化 SQL 执行效率 -> 执行器操存储引擎读取数据、返回数据

连接器

连接器负责客户端跟服务端建立连接、获取权限、维持和管理连接

当我们在客户端执行mysql -u$user -p&pwd命令时,就会向服务器发起一个连接请求。

完成TCP握手后,请求来到连接器这里,连接器首先会进行身份认证,也就是看我们输入的用户名、密码是否正确。

如果不正确,则返回Access denied for user错误给客户端,结束流程。

如果认证通过,连接器会进一步到权限表里查询用户所拥有的全部权限,并加载到内存中,在该连接的生命周期内,权限的判断都基于这里查出来的权限。

也就是说,如果在连接成功后,管理员修改了该用户的权限是不会马上生效的,只有该用户重新连接,重新加载权限才会生效。

连接成功后,如果没有后续动作,这个连接会处于空闲状态(Sleep),并且当长时间没有动作时,Mysql会自动断开该连接,默认等待时长是8小时,可以通过修改 wait_timeout 变量来修改该时长。

长连接和短连接

长链接指连接成功后,后续的请求会一直使用同一个连接。

短连接指在每次执行完很少的几次查询后就断开连接,下次查询再重新连接。

因为连接的建立过程比较复杂,所以建议尽量使用长连接。

使用长连接的好处是减少频繁连接所造成的网络开销和系统开销,缺点是内存的占用可能会不断上涨。

因为 Mysql 在执行过程中所使用的临时内存是管理在连接对象中的,只有当连接断开时,这些使用的资源才会被释放掉。

如果长时间得不到释放,而长连接的不断增加,内存占用也不断上涨,最后可能会触发 OOM (Out Of Memory),导致 Mysql 被系统强行杀死。

针对这种情况,参考的解决方案有两种:

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

查询缓存

Mysql 在每次查询时, 都会先在查询缓存看看有没有缓存,如果有,直接取缓存的结果返回给客户端;如果没有,再继续往下执行。

查询缓存优点是把上一次查询的结果以 key-value 的形式缓存下来,SQL 语句为 key, 结果集为 value。

在下次查询时如果是相同的查询语句,直接从缓存返回去,就不用继续往下执行复杂的逻辑,节省了时间和资源。

读多写少的环境下,可以很大的提升效率。

查询缓存有一个弊端,当对一个表进行更新时,针对这个表的所有缓存都会失效被清空。

所以当在一个写多读少的环境下使用查询缓存,缓存不但利用率不高,反而不断的更新和失效会给数据库带来很大的压力。

所以不建议使用查询缓存,这个功能在 Mysql 8.0 开始也被删除掉了。

分析器

分析器负责对 SQL 语句进行词法分析语法分析

词法分析是分析这条 SQL 语句是什么类型的语句,是查询语句还是更新语句等,然后把 SQL 语句里面的表名和字段名识别出来,并检查表和字段是否存在。

语法分析是分析这条 SQL 语句的语法是否正确,比如 SELECT 这个关键字有没有错,是不是缺少了 FROM 关键字等等。

如果词法或者语法不正确,就会抛出错误给客户端,并结束流程。

优化器

SQL 语句经过分析器分析无误了,就要针对 SQL 语句进行优化,比如在多个索引中,选择哪一个索引效率会更快;JOIN 语句选择哪一张去连接别一张表。

总的来说,优化器做的事就是采取不同的选择、不同的策略去尽可能的让 SQL 语句的执行效率更加高。

执行器

到了执行器,就开始准备操作存储引擎,获取数据,封装结果集。

在开始执行之前,执行器首先会判断用户有没有操作这张表的权限,如果没有则返回错误;有则打开表开始读取数据。

执行的流程大概如下:

  1. 调用 InnoDB 引擎接口读取这个表的第一行,判断这行的数据是否符合查询条件,符合则放到结果集中,不符合则跳过。
  2. 调用引擎接口取下一行,重复 1 的判断逻辑,直到这个表的最后一行。
  3. 执行器把所有满足条件的行封装成结果集返回给客户端。

至此,这个语句就执行完成了。

你可能感兴趣的:(Mysql 在命令行输入 SELECT 语句后,背后发生了什么事?)