MySQL语句是怎么执行的?

你知道 MySQL 语句是怎么执行的吗?

平时我们开发,使用数据库时只知道我们执行 insert 语句 MySQL 会帮我们存储,使用 query 语句 MySQL 会帮我们查询。但是它内部到底是怎么执行的呢?比如,我们有个表 user,表里仅有一个 id 字段,当我们执行以下查询语句时
select * from user where id =5;

我们看到的只是输入了一条语句,MySQL给我们返回了一个结果,却不知道这条语句 MySQL是怎么执行的。今天,我将把 MySQL 的内部拆解一下,看一下一条语句到底是怎么执行的。

下面是MySQL的基本架构示意图。

整体看,MySQL 分为 Server层存储引擎层

连接器

当我们执行语句时,首先会连接到数据库上,数据库用来接待我们的就是连接器。连接器负责和客户端建立连接、获取权限、维持以及管理连接。连接器在经过TCP握手后,会使用我们携带的用户名和密码认证我们的身份,如果验证失败,客户端结束;如果验证通过,连接器会到权限表里查出用户所拥有的权限。在这之后,此连接里的所有操作都将依赖于此时读到的权限。只要这个连接不断开,即使管理员账号对当前连接用户的权限做了修改,也不会影响已经存在的连接的权限。

当我们连接完成,长时间没有执行操作后,连接器会自动断开。这个时间由参数 wait_timeout 控制,默认为 8 小时。因为建立连接的消耗比较大,因此我们在程序中一般会使用长连接,即多个请求一直使用同一个连接。

有时候我们会遇到 MySQL 异常重启的情况,一种可能的原因是我们使用长连接的时间过长。MySQL在执行过程中临时使用的内存是在连接对象里管理的,大部分资源会在连接断开的时候才释放。如果长连接长期累积下来,会导致内存涨的非常快,进而被系统OOM。

遇到这种情况,我们一般有两种方案解决:

  1. 定期断开长连接。程序定时断开长连接,或者等执行完一个占用内存较大的查询后,断开连接。
  2. 在 5.7 版本以后,可以在每次执行完占用内存较大的查询后,主动执行 mysql_reset_connection 来重新初始化连接资源。

查询缓存

建立完连接后,我们就可以开始执行语句了。MySQL拿到我们的请求后,会先到缓存里看下之前是不是执行过这条语句。如果能找到,那么这个结果值会直接返回给客户端。值得注意的是,在返回结果前,会进行权限校验,即我们是否有对此表查询的权限。

但是,大部分情况下,缓存里都不会存在,这是因为,只要我们对一个表执行过更新语句,那么针对这个表的缓存就都失效了。

在 8.0 版本,MySQL 直接将查询缓存的功能移除了。

分析器

如果没有命中缓存,MySQL会开始真正执行语句,首先就是对SQL语句解析。分析器会先做“词法分析”,我们输入的SQL语句由多个字符串和空格组成,MySQL需要识别出里面的字符串分别代表什么。做完了识别后,会进行“语法分析”,根据语法规则判断输入的SQL语句是否满足语法。我们常遇到的 “You have an error in your SQL syntax” 错误就表示语法错了。

优化器

通过分析器,MySQL知道我们要做什么了。但是它还需要知道怎么做能更高效的帮我们完成任务,这就是优化器的任务。优化器会决定使用哪个索引,以及当我们有多表关联时,决定各个表的连接顺序。帮我们确认最优的方案。

执行器

通过优化器,MySQL知道怎么做最优了。接下来,就需要进入执行器,开始执行语句。开始执行前,执行器会先判断我们是否有对表的执行查询的权限,如果没有,会返回权限不足的错误。如果有权限,会打开表继续执行。打开表后,执行器会根据表的引擎定义,去调用此引擎提供的接口。

对于我们刚开始的查询语句,我们的 id 字段没有索引,执行器会按照如下流程进行执行:

  1. 调用 InnoDB 引擎接口取出 user 表的第一行,判断 id 的值是不是 5,如果不是则跳过,否则将此行存在结果集里;
  2. 调用引擎接口取“下一行”,重复1中的判断逻辑,直到所有的行都进行判断。
  3. 执行器会将所有满足条件的行组成结果集返回给客户端。

以上,就是一条 SQL 语句的完整执行流程。


标题MySQL语句是怎么执行的?
作者末日没有进行曲
链接MySQL语句是怎么执行的?
时间:2020-12-14
声明:本博客所有文章均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

# MySQL

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×