文心阁小说本篇文章会分析下一个 sql 语句在 MySQL 中的执行流程,包括 sql 的查询在 MySQL 内部会怎么流转,sql 语句的更新是怎么完成的。java
在分析以前我会先带着你看看 MySQL 的基础架构,知道了 MySQL 由那些组件组成已经这些组件的做用是什么,据说她是校霸罩着能够帮助咱们理解和解决这些问题。git
下图是 MySQL 的一个简要架构图,从下图你能够很清晰的看到用户的 SQL 语句在 MySQL 内部是如何执行的。个人汉服男友
github
先简单介绍一下下图涉及的一些组件的基本做用帮助你们理解这幅图,在 1.2 节中会详细介绍到这些组件的做用。算法
简单学渣同桌不须要安慰来讲 MySQL 主要分为 Server 层和存储引擎层:sql
反派天天喜当爹链接器主要和身份认证和权限相关的功能相关,就比如一个级别很高的门卫同样。数据库
主要负责用户登陆数据库,进行用户的身份认证,包括校验帐户密码,权限等操做,若是用户帐户密码已经过,链接器会到权限表中查询该用户的全部权限,以后在这个链接里的权限逻辑判断都是会依赖此时读取到的权限数据,也就是说,后续只要这个链接不断开,即时管理员修改了该用户的权限,该用户也是不受影响的。缓存
我不作人了查询缓存主要用来缓存咱们所执行的 SELECT 语句以及该语句的结果集。架构
链接创建后,执行查询语句的时候,会先查询缓存,MySQL 会先校验这个 sql 是否执行过,以 Key-Value 的形式缓存在内存中,Key 是查询预计,Value 是结果集。若是缓存 key 被命中,就会直接返回给客户端,若是没有命中,就会执行后续的操做,完成后也会把结果缓存起来,方便下一次调用。固然在真正执行缓存查询的时候仍是会校验用户的权限,是否有该表的查询条件。函数
MySQL 查询不建议使用缓存,由于查询缓存失效在实际业务场景中可能会很是频繁,被五个总裁轮流补习的日子假如你对一个表更新的话,这个表上的全部的查询缓存都会被清空。对于不常常更新的数据来讲,使用缓存仍是能够的。优化
因此,通常在大多数状况下咱们都是不推荐去使用查询缓存的。
MySQL 8.0 版本后删除了缓存的功能,官方也是认为该功能在实际的应用场景比较少,因此干脆直接删掉了。
慕北屹顾小陌MySQL 没有命中缓存,那么就会进入分析器,分析器主要是用来分析 SQL 语句是来干吗的,分析器也会分为几步:
第一步,词法分析,一条 SQL 语句有多个字符串组成,首先要提取关键字,好比 select,提出查询的表,提出字段名,提出查询条件等等。作完这些操做后,就会进入第二步。
第二步,语法分析,主要就是判断你输入的 sql 是否正确,是否符合 MySQL 的语法。
完成这 2 步以后,MySQL 就准备开始执行了,可是如何执行,怎么执行是最好的结果呢?这个时候就须要优化器上场了。
优化器的做用就是它认为的最优的执行方案去执行(有时候可能也不是最优,这篇文章涉及对这部分知识的深刻讲解),樊璃凌月好比多个索引的时候该如何选择索引,多表查询的时候如何选择关联顺序等。
能够说,通过了优化器以后能够说这个语句具体该如何执行就已经定下来。
当选择了执行方案后,MySQL 就准备开始执行了,首先执行前会校验该用户有没有权限,若是没有权限,就会返回错误信息,若是有权限,就会去调用引擎的接口,返回接口执行的结果。
说了以上这么多,那么究竟一条 sql 语句是如何执行的呢?其实咱们的 sql 能够分为两种,一种是查询,一种是更新(增长,更新,删除)。咱们先分析下查询语句,语句以下:
select * from tb_student A where A.age='18' and A.name=' 张三 ';
江瑟瑟结合上面的说明,咱们分析下这个语句的执行流程:
先检查该语句是否有权限,若是没有权限,直接返回错误信息,若是有权限,在 MySQL8.0 版本之前,会先查询缓存,以这条 sql 语句为 key 在内存中查询是否有结果,若是有直接缓存,若是没有,执行下一步。
经过分析器进行词法分析,提取 sql 语句的关键元素,好比提取上面这个语句是查询 select,提取须要查询的表名为 tb_student,须要查询全部的列,查询条件是这个表的 id='1'。而后判断这个 sql 语句是否有语法错误,好比关键词是否正确等等,若是检查没问题就执行下一步。
接下来就是优化器进行肯定执行方案,上面的 sql 语句,能够有两种执行方案:
a.先查询学生表中姓名为“张三”的学生,而后判断是否年龄是 18。 b.先找出学生中年龄 18 岁的学生,而后再查询姓名为“张三”的学生。
那么优化器根据本身的优化算法进行选择执行效率最好的一个方案(优化器认为,有时候不必定最好)。那么确认了执行计划后就准备开始执行了。
进行权限校验,若是没有权限就会返回错误信息,若是有权限就会调用数据库引擎接口,返回引擎的执行结果。
以上就是一条查询 sql 的执行流程,那么接下来咱们看看一条更新语句如何执行的呢?sql 语句以下:
update tb_student A set A.age='19' where A.name=' 张三 ';
闪婚总裁深深爱咱们来给张三修改下年龄,在实际数据库确定不会设置年龄这个字段的,否则要被技术负责人打的。其实条语句也基本上会沿着上一个查询的流程走,只不过执行更新的时候确定要记录日志啦,这就会引入日志模块了,MySQL 自带的日志模块式 binlog(归档日志) ,全部的存储引擎均可以使用,咱们经常使用的 InnoDB 引擎还自带了一个日志模块 redo log(重作日志),咱们就以 InnoDB 模式下来探讨这个语句的执行流程。流程以下:
这里确定有同窗会问,为何要用两个日志模块,用一个日志模块不行吗?
这是由于最开始 MySQL 并没与 InnoDB 引擎( InnoDB 引擎是其余公司以插件形式插入 MySQL 的) ,MySQL 自带的引擎是 MyISAM,可是咱们知道 redo log 是 InnoDB 引擎特有的,其余存储引擎都没有,这就致使会没有 crash-safe 的能力(crash-safe 的能力即便数据库发生异常重启,以前提交的记录都不会丢失),binlog 日志只能用来归档。
并非说只用一个日志模块不能够,只是 InnoDB 引擎就是经过 redo log 来支持事务的。那么,又会有同窗问,我用两个日志模块,可是不要这么复杂行不行,为何 redo log 要引入 prepare 预提交状态?这里咱们用反证法来讲明下为何要这么作?
若是采用 redo log 两阶段提交的方式就不同了,写完 binglog 后,而后再提交 redo log 就会防止出现上述的问题,从而保证了数据的一致性。那么问题来了,有没有一个极端的状况呢?假设 redo log 处于预提交状态,binglog 也已经写完了,这个时候发生了异常重启会怎么样呢? 这个就要依赖于 MySQL 的处理机制了,MySQL 的处理过程以下:
这样就解决了数据一致性的问题。
www.wxgxs.net