扩展一:对于mysql8.0而言,查询缓存已经从数据库server层中移除,原因是对于频繁更新的表,查询缓存的命中太低。查询缓存的逻辑是,对于每条要执行的SQL语句进行hash值计算,如果计算的值在缓存中,则返回值结果。但是每次SQL更新操作都会刷新缓存,这就导致无法利用缓存来做到快速响应,相反还要做一次缓存试探操作。更骚的是,结果集相同但SQL语句写法不同,也会产生不同的hash值,如下两条:

    1. select * from table_t where f1 = 1 and f2 = 2;
    2. select * from table_t where f1 = 2 and f1 = 1;

    上面两条的SQL语句的hash值是不一样的,这就导致缓存了第一条的查询结果在第二条上不适配。


    注:基于mysql8.0以前版本进行分析
    image.pngServer 层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。而存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始成为了默认存储引擎。也就是说,你执行 create table 建表的时候,如果不指定引擎类型,默认使用的就是 InnoDB。不过,你也可以通过指定存储引擎的类型来选择别的引擎,比如在 create table 语句中使用 engine=memory, 来指定使用内存引擎创建表。不同存储引擎的表数据存取方式不同,支持的功能也不同,在后面的文章中,我们会讨论到引擎的选择。

    1. select * from table_t where id = 1

    假设DBMS要执行上面的这条语句,那么DBMS需要经过如下几步操作:

    1. 客户端与server层取的链接,这层由连接器负责。在这一步中,需要经过如下几个操作:
      1. 客户端与服务端采用经典的TCP协议建立网络链接
      2. 要求客户端输入验证信息,可能是token密钥,也可能是用户名和密码。
      3. 连接器分析输入的验证信息,查看数据库中关于此用户的信息
        1. 信息合法:建立链接
        2. 信息不合法:报“Access denied for user”错误(中断以下步骤)
      4. 连接器判断并分配用户权限
    2. 查询缓存,如果缓存中有数据信息,则直接返回查询结果。
    3. 如果缓存中没有数据信息,则将sql语句送入分析器,分析器会进行如下工作:
      1. 首先进行词法分析,将输入的是由多个字符串和空格组成的一条 SQL 语句,MySQL 需要识别出里面的字符串分别是什么,代表什么。MySQL 从你输入的”select”这个关键字识别出来,这是一个查询语句。它也要把字符串“T”识别成“表名 T”,把字符串“ID”识别成“列 ID”。
      2. 其次就是语法分析,mysql会将你的SQL语句与mysql语法做对比分析,并判断:
        1. 语法合法,进行下一步
        2. 语法不合法,报“You have an error in your SQL syntax”错误提示。(一般语法错误会提示第一个出现错误的位置,所以你要关注的是紧接“use near”的内容。)
    4. 经过分析器分析后,就轮到优化器了。分析器将sql语句分析后,让mysql明白自己要做什么事,而优化器的作用是让mysql明白怎么做。优化器是在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序。比如你执行下面这样的语句,这个语句是执行两个表的 join:
      1. mysql> select * from t1 join t2 using(ID) where t1.c=10 and t2.d=20;
    • 既可以先从表 t1 里面取出 c=10 的记录的 ID 值,再根据 ID 值关联到表 t2,再判断 t2 里面 d 的值是否等于 20。
    • 也可以先从表 t2 里面取出 d=20 的记录的 ID 值,再根据 ID 值关联到 t1,再判断 t1 里面 c 的值是否等于 10。

    这两种执行方法的逻辑结果是一样的,但是执行的效率会有不同,而优化器的作用就是决定选择使用哪一个方案。优化器阶段完成后,这个语句的执行方案就确定下来了,然后进入执行器阶段。如果你还有一些疑问,比如优化器是怎么选择索引的,有没有可能选择错等等,没关系,我会在后面的文章中单独展开说明优化器的内容。

    1. 经过分析器分析后,mysql知道自己干什么;经过优化器优化后,mysql知道了自己该怎么做;知道做什么,也知道怎么做,就轮到一个执行者了,就是执行器了。执行器会经过如下几步:
      1. 判断用户的权限,即precheck:(其实在查询缓存的时候,就有权限判断逻辑)
        1. 有权限,执行以下步骤
        2. 无权限,报无权限错误。如下例子所示:ERROR 1142 (42000): SELECT command denied to user ‘b’@’localhost’ for table ‘T’
      2. 如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。
      3. 对于执行器,他的执行流程入戏所示:
        1. 调用引擎接口(默认 innodb)取表table_t的第一行,判断ID是否等于1,如果等于1,则将语句查出来放入结果集中。
        2. 调用引擎接口判断下一行,重复执行直至最后一行
        3. 执行器将所有的满足上条件的结果集封装起来,反馈给客户端。至此整个执行流程走完了。

    对于有索引的表,执行的逻辑也差不多。第一次调用的是“取满足条件的第一行”这个接口,之后循环取“满足条件的下一行”这个接口,这些接口都是引擎中已经定义好的。你会在数据库的慢查询日志中看到一个 rows_examined 的字段,表示这个语句执行过程中扫描了多少行。这个值就是在执行器每次调用引擎获取数据行的时候累加的。在有些场景下,执行器调用一次,在引擎内部则扫描了多行,因此引擎扫描行数跟 rows_examined 并不是完全相同的。