重读MySQL45讲001——一条SQL查询语句是如何执行的

128 阅读6分钟

写在前面

读书笔记,只想做个记录。往来的人随便看看,随便读读就好。

先前的Kafka系列会先告一段落。技术的海洋太宽广,作为一名普通的后端,我希望能通过写作可能培养学习的习惯,小步快跑地完成这场马拉松。所以,如果对于日常后端开发的所有涉及技术组件我都希望能有所学习。因此通过重读之前学习的Mysql45讲,顺带复习一下。

这篇文章是学习的MySQL45讲的第一讲。

文章脉络

第1讲以「一条查询SQL语句是如何执行的?」问题切入。通过这个最简单、最常见的SQL语句执行,来整体(宏观)了解MySQL背后的机制。因此,这一讲重点是引入MySQL的整体架构,与关键模块的概念、作用。

MySQL整体架构

可以通过如下的结构展示:

  • 客户端
  • MySQL服务端
    • Server层
      • 连接器
      • 分析器
      • 优化器
      • 执行器
    • 存储引擎层
      • InnoDB
      • MyISAM
      • Memory

MySQL对外是以经典的C/S架构提供服务的。客户端通过连接到MySQL服务端,通过向服务端发送各种指令,来实现各种功能服务。

MySQL内部可先分成两大块:

  1. Server层
  2. 存储引擎层

这也体现MySQL的架构设计思想。RDS数据库的存储与提取能力由存储引擎层实现。基于不同的存储与查询技术实现的存储引擎是一种类似可「水平扩展」的数据管理能力。而不同的数据管理能力也能以插件式集成到MySQL整体架构中。

除开数据管理能力以外的其他功能,被MySQL集中到了Server层,比如:连接鉴权、连接管理、SQL分析、SQL优化、SQL函数、各种跨引擎能力。而Server层根据SQL执行过程,也拆分成了:连接器、分析器、优化器、执行器。

连接器

连接器负责客户端与MySQL服务端的连接,包括:连接的建立、鉴权、维持、管理等。

文章中针对这一节提了这几个重点:

  1. 连接时不建议在连接命令中通过「-p」参数明文输入密码。实际在生成环境中,会通过公司级别的服务发现体系,通过console以服务发现与注册的方式,实现客户端服务与MySQL服务的链接,不太能用到密码明文输入的场景。

  2. 长连接还是短连接:MySQL与客户端的连接是基于TCP协议的,建立链接是比较耗费资源的,所以一般会通过长连接与连接池,维护俞MySQL链接的多个长连接。

  3. 基于2,如果长时间使用长连接,文章提到了因为MySQL在执行过程的内存对象是管理在连接对象的,所以在某些特殊情况下,过长的长连接与大量数据操作可能会导致MySQL实例的快速上涨,进而导致OOM与MySQL实例的异常重启。针对这种问题,文章提出了两种解决方案:

    1.断开长链接与重连 :执行时机有两个:a. 定时触发;b. 业务判断执行大数据量操作之后; 2.MySQL5.7以后的版本可通过「mysql_reset_connection」来重新初始化链接状态。这个过程不需要重连与鉴权,只是恢复建立链接时的初始状态

查询缓存

建立连接之后就是正式进入SQL语句执行过程,但首先MySQL8.0版本以前还会有查询缓存。MySQL会在内存中通过K-V维护缓存,K为查询的SQL语句,V为上一次语句返回的结果。但这个过程性价比不高,所以文章也没有赘述。主要缺点在于:

  1. MySQL查询的缓存失效可能会非常频繁,根因在于很多场景表中数据会有频繁的更新,而每次更新都会导致缓存的失效。除非是读多写少的场景,或者说业务表是类似「静态」的数据。

所以MySQL的查询缓存不是一种普遍的缓存提升性能的方案,可以考虑针对「静态」数据,配合Redis的缓存,再考虑利用MySQL的查询缓存,作为缓存穿透后的最后一道保障屏障。

所以如果需要使用查询缓存,可以在select之后,加上「SQL_CACHE」来显式指明这条SQL需要使用缓存。

分析器

告诉MySQL执行一条语句分为三步:1. 要做什么?2. 怎么做?3. just do it。

所以分析器就是解决第一步:「要做什么」。这一步,MySQL其实就是解析SQL语句

  1. 词法分析:分析器基于SQL是关键词+空格的构造格式,将一条SQL语句拆分成多个关键词。比如:select、from、where等。而后构成语法树,进入到语法分析
  2. 语法分析:基于语法树判断这条SQL语句是否合法,比如标识符关键字是否有效、数据表名与列名是否存在、别名是否有效等,检查通过之后会再生成一棵新的解析树传给优化器。

优化器

优化器就是解决第二步:「怎么做」。在分析器检查过SQL是否可执行之后,在实现同一SQL查询效果的多种方案之间选择最优的执行方案。具体优化器是怎么做到的,第一讲没细说。

执行器

执行器就解决最后一步:「just do it」。额外需要注意有一点:执行器会判断具体执行的SQL是否有表执行权限。

在通过鉴权后,Server层就会根据目标表指定的引擎,去调用引擎接口,完成SQL的执行,具体如下:

  1. 调用引擎接口获取表数据的第一行,判断是否满足SQL查询条件,如果满足,就记录在内存中,不满足就跳过;
  2. 调用引擎的「下一条」接口,获取后面的数据,重复1过程中的判断。直到取到表的最后一行。
  3. 将上面过程执行完之后的最终结果返回给客户端。

文章在执行器还提到了一个细节:explain中的「rows_examined」就是调用引擎获取数据接口返回的数据行数累加计算得到的。

写在最后

第一讲的思路还是非常的清晰,偏向基础架构说明与关键角色的概念说明。常读常新,温故知新。