MySQL的存储引擎(一条sql语句的执行流程是什么样的?)
目录
一、指引(MySQL体系结构)、
1. Server层
1.1. 连接器
1.2. 查询缓存
1.3. 解析器
1.4. 预处理器
1.5. 优化器
1.6. 执行器
2. 2. 存储引擎层
2.1. InnoDB
2.2. MyISAM
2.3. Memory
二、引擎之间的对比
三、存储引擎的选择
四、面试重点: 一条 update 语句执行流程
五、自测题
一、指引(MySQL体系结构)、
- 图源 : 执行一条 select 语句,期间发生了什么?
show engines; # 查看mysql支持的引擎show table status from 数据库名; # 查看某个数据库下的所有表的引擎信息show create table 表名; # 查看某个表的引擎信息
1. Server层
主要包括连接器、查询缓存、解析器、预处理器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。
下面我们来看下Server层的连接器、查询缓存、分析器、优化器、执行器分别主要干了哪些事情。
1.1. 连接器
我们知道由于MySQL是开源的,他有非常多种类的客户端:navicat,mysql front,jdbc,SQLyog等非常丰富的客户端,包括各种编程语言实现的客户端连接程序,这些客户端要向mysql发起通信都必须先跟Server端建立通信连接,而建立连接的工作就是有连接器完成的。
第一步,你会先连接到这个数据库上,这时候接待你的就是连接器。连接器负责跟客户端建立连接、获取权限、维持和管理连接。连接命令一般是这么写的:
[root@192 ~]# mysql -h host[数据库地址] -u root[用户] -p root[密码] -P 3306
连接命令中的 mysql 是客户端工具,用来跟服务端建立连接。在完成经典的 TCP 握手后,连接器就要开始认证你的身份,这个时候用的就是你输入的用户名和密码。
- 1、如果用户名或密码不对,你就会收到一个"Access denied for user"的错误,然后客户端程序结束执行。
- 2、如果用户名密码认证通过,连接器会到权限表里面查出你拥有的权限。
- 3、 之后,这个连接里面的权限判断逻辑,都将依赖于此时读到的权限。
这就意味着,一个用户成功建立连接后,即使你用管理员账号对这个用户的权限做了修改,也不会影响已经存在连接的权限。修改完成后,只有再新建的连接才会使用新的权限设置。
1.2. 查询缓存
连接建立完成后,你就可以执行 select 语句了。执行逻辑就会来到第二步:查询缓存。
MySQL 拿到一个查询请求后,会先到查询缓存看看,之前是不是执行过这条语句。之前执行过的语句及其结果可能会以 key-value 对的形式,被直接缓存在内存中。key 是查询的语句,value 是查询的结果。如果你的查询能够直接在这个缓存中找到 key,那么这个 value 就会被直接返回给客户端。
如果语句不在查询缓存中,就会继续后面的执行阶段。执行完成后,执行结果会被存入查询缓存中。你可以看到,如果查询命中缓存,MySQL 不需要执行后面的复杂操作,就可以直接返回结果,这个效率会很高。
大多数情况查询缓存就是个鸡肋,为什么呢?
1. 缓存失效频繁
- 数据更新影响:在数据库中,一旦对某个表执行了
INSERT
、UPDATE
、DELETE
等更新操作,该表相关的所有查询缓存都会被清空。例如,在一个电商数据库里,商品表可能会频繁更新商品价格、库存等信息。每次更新操作后,所有涉及商品表的查询缓存都会失效,即使这些查询的结果实际上并没有受到此次更新的影响。 - 元数据变化影响:除了数据更新,表结构的修改(如添加、删除列等)也会导致查询缓存失效。例如,在一个用户信息表中添加了一个新的字段,那么所有与该表相关的查询缓存都会被清除。
2. 查询缓存的局限性
- 不支持所有查询:并非所有的查询都可以被缓存。例如,包含不确定函数(如
NOW()
、RAND()
等)的查询,由于每次执行的结果都可能不同,无法被缓存。此外,一些复杂的存储过程和触发器中的查询也可能无法被缓存。 - 大小写和空格敏感:查询缓存是大小写和空格敏感的。这意味着即使两个查询的逻辑相同,但由于大小写或空格的差异,也会被视为不同的查询,无法共享缓存结果。缓存命中率低...
查看当前mysql实例是否开启缓存机制
mysql> show global variables like "%query_cache_type%";
注意 : mysql 8.0已经移除了查询缓存功能
1.3. 解析器
如果没有命中查询缓存,就要开始真正执行语句了。首先,MySQL 需要知道你要做什么,因此需要对 SQL 语句做解析。
分析器先会做“词法分析”。你输入的是由多个字符串和空格组成的一条 SQL 语句,MySQL 需要识别出里面的字符串分别是什么,代表什么。
MySQL 从你输入的"select"这个关键字识别出来,这是一个查询语句。
然后要做“语法分析”。根据词法分析的结果,语法分析器会根据语法规则,判断你输入的这个 SQL 语句是否满足 MySQL 语法。
如果你的 sql 语法不对,就会收到“You have an error in your SQL syntax”的错误提醒,比如下面这个语句 from 写成了 "rom"。
mysql> select * fro test where 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 'fro test where id=1' at line 1
SQL语句经过分析器分析之后,会生成一个这样的语法树
至此我们分解析器的工作任务也基本圆满了。
经过解析器后,接着就要进入执行 SQL 查询语句的流程了,每条SELECT
查询语句流程主要可以分为下面这三个阶段:
- prepare 阶段,也就是预处理阶段 [预处理器]
- optimize 阶段,也就是优化阶段 [优化器]
- execute 阶段,也就是执行阶段 [执行器]
1.4. 预处理器
sql 语法的判断对错是解析器完成的,那么 sql 语句中写的表是否存在呢,字段是否存在?
判断表或字段是否存在的工作被放到了 prepare 阶段来操作的
prepare 阶段主要干了俩件事:
- 检查 SQL 查询语句中的表或者字段是否存在
- 将
select *
中的*
符号,扩展为表上的所有列
下面这条查询语句,test 这张表是不存在的,这时 MySQL 就会在执行 SQL 查询语句的 prepare 阶段中报错
select * from test;
报错 : ERROR 1146 (42S02): Table 'mysql.test' doesn't exist
1.5. 优化器
经过预处理阶段后,还需要为 SQL 查询语句先制定一个执行计划,这个工作交由「优化器」来完成
优化器主要负责将 SQL 查询语句的执行方案确定下来,比如在表里面有多个索引的时候,优化器会基于查询成本的考虑,来决定选择使用哪个索引
当然,本次的查询语句(select * from product where id = 1)选择使用主键索引
要想知道优化器选择了哪个索引,可以在查询语句最前面加个 explain
命令,这样就会输出这条 SQL 语句的执行计划,然后执行计划中的 key 就表示执行过程中使用了哪个索引,比如下图的 key 为 PRIMARY
就是使用了主键索引
如果查询语句的执行计划里的 key 为 null 说明没有使用索引,那就会全表扫描(type = ALL),这种查询扫描的方式是效率最低档次的,如下图:
优化器总结: 是在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序;(小表驱动大表,减少中间结果集的大小) 以及一些mysql自己内部的优化机制。
1.6. 执行器
开始执行的时候,要先判断一下你对这个表 T 有没有执行查询的权限,如果没有,就会返回没有权限的错误,如下所示 (在工程实现上,如果命中查询缓存,会在查询缓存返回结果的时候,做权限验证)。
mysql> select * from test where id=10;
如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。
2. 2. 存储引擎层
存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始成为了默认存储引擎。也就是说如果我们在create table时不指定表的存储引擎类型,默认会给你设置存储引擎为InnoDB。
2.1. InnoDB
介绍
- InnoDB是一种兼顾高可靠性和高性能的通用存储引擎,在MySQL5.5后,InnoDB为默认MySQL存储引擎
特点
- 支持事务
- 行级锁,提高并发访问性能
- 支持外键约束,保证数据的完整性和正确性
文件
- xxx.ibd --> xxx代表的是表名,InnoDB引擎的每张表对应一个表空间文件
- Windows中数据存放位置: C:\ProgramData\MySQL\MySQL Server 8.0\Data
2.2. MyISAM
介绍
- MyISAM是MySQL早期的默认存储引擎
特点
- 访问速度快 : 索引和数据表是分开的,索引存的是数据表的引用地址,访问速度快
- 支持表锁,不支持行锁
- 不支持事务、外键
文件
- xxx.sdi --> 存储表的结构信息
- xxx.MYD --> 存储数据
- xxx.MYI --> 存储索引
2.3. Memory
介绍
- Memory引擎的表数据是存储在内存中的,由于受到硬件问题、断电的影响,只能将这些表作为临时表或缓存使用
特点
- 数据存放在内存中
- hash索引(默认)
文件
- xxx.sdi --> 存储表结构信息
- 它的数据保存在内存中
二、引擎之间的对比
- InnoDB和MyISAM的区别?
-
- 事务,外键: InnoDB支持事务,外键,MyISAM不支持事务,外键
- 锁粒度: InnoDB最小粒度锁是行锁,MyISAM最小粒度锁是表锁
- 索引结构:
-
-
- InnoDB有聚餐索引,MyISAM是非聚餐索引
- InnoDB的索引和数据都放在同一个文件下,后缀为 .ibd
- MyISAM的索引和数据是分开的,索引放在文件后缀为 .MYI,数据放在文件后缀为 .MYD
-
-
- count的效率 :
-
-
- InnoDB不保存具体数据的个数,count(*)走全表扫描
- MyISAM 设置了一个变量保存了表中所有数据个数
-
三、存储引擎的选择
- Memory引擎主要作用就是缓存,读取数据速度快
- 但是我们一般使用Redis缓存替代了MySQL的Memory引擎的使用
四、面试重点: 一条 update 语句执行流程
- 客户端通过TCP/IP协议,发送SQL
- 连接器权限验证通过,将sql指令发送给解析器
- 解析器进行词法分析、语法分析,生成语法树,方便后续模块读取表名、字段、sql类型,再进入到预处理器
- 预处理会进行语义分析,检查表名或者字段名是否存在,然后进入到优化器
- 优化器会根据查询成本,看看是否走索引,并生成最优执行计划,交给执行器去执行
- 执行器会先从Buffer Pool中查找数据,如果有数据就返回给执行器,没有数据就从磁盘加载数据返回给执行器
- 此时undo log 开始记录,将修改前的数据写入undo log
- 然后执行器修改数据更新到Buffer Pool中,同时写入redo log buffer,这里采用WAL(Write-Ahead-Log)技术,日志先写技术,在合适的时间将修改的行数据顺序写入磁盘
- 此时已经完成了数据更新
- 然后执行器进行binlog写入磁盘(这里涉及到两阶段提交)
五、自测题
TODO
- 你讲一下mysql的架构?
-
- Server+组件和引擎层+哪些引擎
- mysql的查询缓存为什么会在8.0被弃用?
-
- 回答一下为什么这么鸡肋就行,前面有讲解 -> 大小写敏感 , 空格
- 你讲一下mysql一条 update 语句的执行流程?