MySQL 逻辑架构

从整体架构切入MySQL的学习,可以对 MySQL 有一个整体的把握,鸟瞰全貌,不至于一下陷入细节里边, 对于以后深入理解 MySQL 也是有很大帮助的。本文通过讲解一条查询语句在 MySQL 内部的执行过程,让您对 MySQL 逻辑架构有更深的了解。

1. 逻辑架构组成

图片描述上方是 MySQL 逻辑架构的简单示意图,总的来说,MySQL 包含 Server 层和存储引擎层两大部分。

Server 层包括连接池、查询缓存、解析器、优化器、执行器等,MySQL 的核心服务都在这一层。

存储引擎层负责数据的存储和提取,MySQL 采用插件式的存储引擎,常见的存储引擎有 InnoDB、MyISAM、 CSV 等。其中 InnoDB 是最常用的存储引擎,也是 MySQL 的默认存储引擎(从5.5.5版本开始)。

2. 一条查询语句是如何执行的

上一小节介绍了 MySQL 的逻辑架构组成,那么各模块之间是如何协同工作的呢?这里以一条最简单的查询语句为例子,我们一起来看看这条语句在 MySQL 内部是如何执行的。

select id from a where id=1;

2.1 连接池

首先,我们通过 mysql 这个客户端工具进行数据库的连接,这时遇到的是连接池。连接池负责客户端的连接管理、授权认证。

连接命令如下(输入完连接命令后,需要输入用户密码):

mysql -h localhost -u root -p
  • 如果用户名和密码认证通过,连接池会通过权限表获取这个用户名所拥有的权限信息;

  • 如果用户名或密码认证不通过,则会收到一个错误提示:“ERROR 1045 (28000): Access denied for user ‘root’@‘localhost’”;

建立连接后,会产生相应的连接信息,可以通过 show processlist 命令查看。下方图中 Id 为 5048 这一行,即为成功连接数据库所建立的连接信息,请注意 Command 这一列,值为”Sleep“,表明这是一个空闲连接。成功连接数据库后,如果没有任何动作,这个连接就会变成空闲状态。

root@localhost [(none)]>show processlist;
+------+------+-----------+------+---------+------+----------+------------------+
| Id   | User | Host      | db   | Command | Time | State    | Info             |
+------+------+-----------+------+---------+------+----------+------------------+
| 4771 | root | localhost | NULL | Query   |    0 | starting | show processlist |
| 5048 | root | localhost | NULL | Sleep   |    3 |          | NULL             |
+------+------+-----------+------+---------+------+----------+------------------+
2 rows in set (0.00 sec)

2.2 查询缓存

连接成功建立后,来到第二步查询缓存。查询缓存负责将执行过的语句和结果缓存在内存中。

在获取一个查询请求后,MySQL会先到查询缓存进行查看

  • 如果select语句在查询缓存中能够找到,则直接返回结果给客户端,跳过解析、优化、执行阶段。
  • 如果select语句没能在查询缓存中找到,则继续后面的解析、优化、执行阶段。

从这里可以看到,如果命中查询缓存,MySQL 会直接返回结果给客户端,后面的一系列操作不需要再执行,是非常高效的。但实际情况并非如此,查询缓存非常容易失效

因为只要一个表有更新操作,那这个表所有的查询缓存都会被清空。对一个承载正常业务的数据库来说,更新操作是非常频繁的,这就意味着查询缓存经常失效,从而导致查询缓存的命中率非常低。所以,使用查询缓存反而会给数据库带来额外的负担,在实际生产环境中,我们建议关闭查询缓存

关闭查询缓存的方法有两种:

  • 临时:在 MySQL 中直接用命令行执行;

    set global query_cache_size=0
    set global query_cache_type=0
    
  • 永久:将以下两个参数添加至配置文件 my.cnf,并重启 MySQL;

    query_cache_type=0
    query_cache_size=0
    

2.3 解析器

如果没有命中查询缓存,接下来就要进入解析器阶段了。解析器负责词法解析和语法解析。

首先是词法解析,MySQL 需要识别所输入的字符串分别代表什么,它会从左到右一个字符、一个字符地输入,然后根据构词规则识别单词。

select id from a where id=1;
关键字 非关键字 关键字 非关键字 关键字 非关键字
select 字段id from 表名a where 字段id等于1

接下来是语法解析,判断输入的这个SQL语句是否符合MySQL语法规则。如果语法不对,会收到错误信息提示:“ERROR 1064 (42000): You have an error in your SQL syntax;”。如下面这个SQL语句的where少了一个e。

root@localhost [tempdb]>select id from a wher id=1;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'id=1' at line 1

2.4 优化器

从解析器出来,就到了优化器阶段。优化器负责找到最优的执行计划,也就是决定SQL语句的执行方案。

一条查询可以有很多种执行方式,最后都返回相同的结果 。比如下面这个查询SQL,查询表a中字段id等于1的值

select id from a where id=1;
  • 可以遍历表a所有行,找出所有id等于1的值
  • 也可以通过索引idx_id,找到id等于1的值。当然,前提是字段id有创建索引idx_id。

两种方案的结果是一样的,但是执行效率不一样,优化器的作用就是选择最优的执行方案。

2.5 执行器

现在到了执行SQL语句的阶段,也就是执行器。执行器负责调用存储引擎,拿到查询结果。

select id from a where id=1;

假设这个例子中的字段id没有索引,执行器的流程大致如下:

  1. 调用 InnoDB 引擎接口获取表 a 的第一行,如果 id 值等于 1,则将 id 值存进结果集,如果 id 值不等于 1,则跳过,取下一行;
  2. 调用 InnoDB 引擎接口获取下一行,重复第一步的逻辑,一直到表 a 的最后一行;
  3. 将符合查询条件的结果集返回给客户端。

3. 小结

一条 SQL 查询的过程,大致就是这样的流程:连接池、查询缓存、解析器、优化器、执行器。

连接池负责连接管理,查询缓存建议关闭,解析器让 MySQL 知道要做什么,优化器让 MySQL 知道怎么做,执行器负责执行取数。

通过本文的讲解,对您认识 MySQL 逻辑架构,有所帮助吗?欢迎反馈宝贵意见,以便我们不断改进。