一条SQL语句在MySQL中是如何执行的

前言

最近开始在学习mysql相关知识,本身根据学到的知识点,根据本身的理解整理分享出来,本篇文章会分析下一个sql语句在mysql中的执行流程,包括sql的查询在mysql内部会怎么流转,sql语句的更新是怎么完成的。在分析以前我会先带着你看看 MySQL 的基础架构,知道了 MySQL 由那些组件组成已经这些组件的做用是什么,能够帮助咱们理解和解决这些问题。mysql

1、mysql架构分析

下面是mysql的一个简要架构图:算法

mysql主要分为Server层和存储引擎层sql

Server层:主要包括链接器、查询缓存、分析器、优化器、执行器等,全部跨存储引擎的功能都在这一层实现,好比存储过程、触发器、视图,函数等,还有一个通用的日志模块 binglog日志模块。数据库

存储引擎: 主要负责数据的存储和读取,采用能够替换的插件式架构,支持InnoDB、MyISAM、Memory等多个存储引擎,其中InnoDB引擎有自有的日志模块redolog 模块。缓存

InnoDB 5.5.5版本做为默认引擎。bash

链接器架构

主要负责用户登陆数据库,进行用户的身份认证,包括校验帐户密码,权限等操做,若是用户帐户密码已经过,链接器会到权限表中查询该用户的全部权限,以后在这个链接里的权限逻辑判断都是会依赖此时读取到的权限数据,也就是说,后续只要这个链接不断开,即时管理员修改了该用户的权限,该用户也是不受影响的。函数

查询缓存学习

链接创建后,执行查询语句的时候,会先查询缓存,Mysql会先校验这个sql是否执行过,以Key-Value的形式缓存在内存中,Key是查询预计,Value是结果集。若是缓存key被命中,就会直接返回给客户端,若是没有命中,就会执行后续的操做,完成后也会把结果缓存起来,方便下一次调用。固然在真正执行缓存查询的时候仍是会校验用户的权限,是否有该表的查询条件。优化

Mysql 查询不建议使用缓存,由于对于常常更新的数据来讲,缓存的有效时间过短了,每每带来的效果并很差,对于不常常更新的数据来讲,使用缓存仍是能够的,Mysql 8.0 版本后删除了缓存的功能,官方也是认为该功能在实际的应用场景比较少,因此干脆直接删掉了。

分析器

mysql 没有命中缓存,那么就会进入分析器,分析器主要是用来分析SQL语句是来干吗的,分析器也会分为几步:

第一步,词法分析,一条SQL语句有多个字符串组成,首先要提取关键字,好比select,提出查询的表,提出字段名,提出查询条件等等。作完这些操做后,就会进入第二步。

第二步,语法分析,主要就是判断你输入的sql是否正确,是否符合mysql的语法。

完成这2步以后,mysql就准备开始执行了,可是如何执行,怎么执行是最好的结果呢?这个时候就须要优化器上场了。

优化器

优化器的做用就是它认为的最优的执行方案去执行(虽然有时候也不是最优),好比多个索引的时候该如何选择索引,多表查询的时候如何选择关联顺序等。

执行器

当选择了执行方案后,mysql就准备开始执行了,首先执行前会校验该用户有没有权限,若是没有权限,就会返回错误信息,若是有权限,就会去调用引擎的接口,返回接口执行的结果。

2、语句分析

2.1 查询语句

说了以上这么多,那么究竟一条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岁的学生,而后再查询姓名为“张三”的学生。
    复制代码

    那么优化器根据本身的优化算法进行选择执行效率最好的一个方案(优化器认为,有时候不必定最好)。那么确认了执行计划后就准备开始执行了。

  • 进行权限校验,若是没有权限就会返回错误信息,若是有权限就会调用数据库引擎接口,返回引擎的执行结果。

2.2 更新语句

以上就是一条查询sql的执行流程,那么接下来咱们看看一条更新语句如何执行的呢?sql语句以下:

update tb_student A set A.age='19' where A.name='张三';
复制代码

咱们来给张三修改下年龄,在实际数据库确定不会设置年龄这个字段的,否则要被技术负责人打的。其实条语句也基本上会沿着上一个查询的流程走,只不过执行更新的时候确定要记录日志啦,这就会引入日志模块了,mysql 自带的日志模块式binlog(归档日志),全部的存储引擎均可以使用,咱们经常使用的InnoDB引擎还自带了一个日志模块redo log,咱们就以InnoDB模式下来探讨这个语句的执行流程。流程以下:

  • 先查询到张三这一条数据,若是有缓存,也是会用到缓存。
  • 而后拿到查询的语句,把 age 改成19,而后调用引擎API接口,写入这一行数据,InnoDB引擎把数据保存在内存中,同时记录redo log,此时redo log进入prepare状态,而后告诉执行器,执行完成了,随时能够提交。
  • 执行器收到通知后记录binlog,而后调用引擎接口,提交redo log 为提交状态。
  • 更新完成。

这里确定有同窗会问,为何要用两个日志模块,用一个日志模块不行吗?这就是以前mysql的模式了,MyISAM引擎是没有redo log的,那么咱们知道它是不支持事务的,因此并非说只用一个日志模块不能够,只是InnoDB引擎就是经过redo log来支持事务的。那么,又会有同窗问,我用两个日志模块,可是不要这么复杂行不行,为何redo log 要引入prepare预提交状态?这里咱们用反证法来讲明下为何要这么作?

  • 先写redo log 直接提交,而后写 binlog,假设写完redo log 后,机器挂了,binlog日志没有被写入,那么机器重启后,这台机器会经过redo log恢复数据,可是这个时候bingog并无记录该数据,后续进行机器备份的时候,就会丢失这一条数据,同时主从同步也会丢失这一条数据。
  • 先写binlog,而后写redo log,假设写完了binlog,机器异常重启了,因为没有redo log,本机是没法恢复这一条记录的,可是binlog又有记录,那么和上面一样的道理,就会产生数据不一致的状况。

若是采用redo log 两阶段提交的方式就不同了,写完binglog后,而后再提交redo log就会防止出现上述的问题,从而保证了数据的一致性。那么问题来了,有没有一个极端的状况呢?假设redo log 处于预提交状态,binglog也已经写完了,这个时候发生了异常重启会怎么样呢? 这个就要依赖于mysql的处理机制了,mysql的处理过程以下:

  • 判断redo log 是否完整,若是判断是完整的,就当即提交。
  • 若是redo log 只是预提交但不是commit状态,这个时候就会去判断binlog是否完整,若是完整就提交 redo log, 不完整就回滚事务。

这样就解决了数据一致性的问题。

3、总结

  • Mysql 主要分为Server曾和引擎层,Server层主要包括链接器、查询缓存、分析器、优化器、执行器,同时还有一个日志模块(binlog),这个日志模块全部执行引擎均可以共用。
  • 引擎层是插件式的,目前主要包括,MyISAM,InnoDB,Memory等。
  • 查询语句的执行流程以下:权限校验(若是命中缓存)---》查询缓存---》分析器---》优化器---》权限校验---》执行器---》引擎
  • 更新语句执行流程以下:分析器----》权限校验----》执行器---》引擎---redo log(prepare 状态---》binlog---》redo log(commit状态)

4、参考

相关文章
相关标签/搜索