本章重点

  • 理解 MySQL 的架构
  • 大致了解一条 MySQL 查询指令的运行过程
  • 理解 MySQL 架构中各个部分的基本作用

MySQL 基本架构

由于对 MySQL 的薄弱认知,我在学习之前甚至对 MySQL 的架构一无所知,不过在看了这张图后到是很快理解了其架构的意义,毕竟根据逻辑来理解,架构总是大同小异的。

架构示意图

可以看到,MySQL 的架构分为两部分:

  • Server 层 : 实现了 MySQL 的大部分核心服务功能,以及所有内置函数
  • 存储引擎:负责数据的存储提取 ,架构模式是插件式,支持多种存储引擎,常用存储引擎包括:
    • InnoDB
    • MyISAM
    • Memory

这也非常符合程序设计的理念:将对数据的存储单独抽象出去,不与"业务逻辑"相耦合,一些语法分析,语法优化的工作交给 Server 去做,存储引擎专心与数据的存取打交道就好,同时不同的存储引擎就像不同的数据结构,可以适用于不同的业务场景需要。

下面开始逐个介绍 Server 层的组件:

连接器

连接器:顾名思义,与客户端打交道的第一个组件。
连接器的功能:

  • 与客户端建立连接
  • 获取客户端的执行权限
  • 维持和管理连接

连接 MySQL 的命令如下:

mysql -h$ip -p$port -u$user -p

这里连接使用到的底层网络协议是 TCP/IP 协议,所以存在三次握手过程,建立连接之后开始校验用户名和密码:

  • 如果用户名/密码错误,抛出 "Access denied for user" 错误,连接结束
  • 如果用户名密码正确,下一步就是 判断用户权限,具体执行逻辑是去 权限表 中查出当前登录用户的权限。
  • 需要注意的是,权限表是快照,所以如果修改了用户的权限,之前已经建立的连接的用户权限是不会改变的,只有新连接的用户的权限才会改变。

空连接: 连接完成后如果没有后续动作,这个连接将处于空闲状态, 使用 show processlist 可以看到所有连接,下面是我查询本地的连接,可以看到 Commond 一行为 Sleep 状态的连接就是 "空闲连接"
image-20200901144205393

连接自动断开时间: 根据 wait_time 参数来 定,超过这个参数还没有动作的连接会被连接器自动断开,默认值是 8小时

数据库中也存在长连接和短链接:

  • 长连接:连接成功之后,客户端持续使用同一个连接
  • 短链接:每个连接执行几次之后就断开,之后重新建立连接

建立连接的过程比较复杂,所以应该减少建立连接的动作,尽量使用长连接。

事分两面,使用长连接也有弊端:MySQL 执行过程中使用的内存以 连接对象 为单位进行管理,一个连接长时间不释放会导致内存过大,OOM,MySQL 服务异常重启。

解决长连接无法释放临时查询内存的方案:

  • 定期断开,或者在程序判断完成一个占用大量内存的查询后断开连接
  • MySQL 的版本如果高于 5.7 ,可以食欲 mysql_reset_connection ,初始化连接资源,释放查询占用的临时内存

查询缓存

MySQL 8.0 之后查询缓存已经不存在了,因为这是一个大部分情况下用处不大的功能。

什么是查询缓存?

查询完成后会有一个视图,如果之后再次进行查询可以加速。但是这个视图的失效条件是表中有数据被修改,只要被修改查询缓存就会失效。

  • MySQL 提供了按需使用查询缓存的方式:
    • query_cache_type 设置为 DEMAND ,默认语句不使用查询缓存,需要使用时可以手动在 sql 语句中指定:
mysql> select SQL_CACHE * from T where ID = 10;

分析器

判断是否有查询缓存之后流程就到了分析器,分析器的作用:

  • 对 SQL 语句进行词法分析语法分析,如果语法不对,则报错。

优化器

如果 SQL 语句中存在多个表关联,则 优化器会对语句进行优化,在不影响结果的情况下决定各个表的连接顺序。【这课太眼熟了,跟 Java 的编译器在不影响最终结果的情况下对代码进行优化是一样的。】

执行器

终于到了最终执行的阶段,执行器是与存储引擎进行交互的组件,其具体的工作流程:

  • 先判断当前连接是否有对具体查询表 T 的查询权限,如果没有则抛出异常。
  • 如果权限正确,则继续执行,打开对应的表时可以知道这个表使用的哪种具体的存储引擎,调用对应的接口即可。

Q.E.D.

知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议

最是人间留不住,曾是惊鸿照影来。