MySQL执行流程

igxiaoshan Lv5

MySql基础架构

  • 连接器: 身份认证和权限相关(登录mysql的时候)
  • 查询缓存: 执行查询语句的时候,会先查询缓存(Mysql 8.0版本后移除,因为这个功能不太实用)
  • 分析器: 没有名字缓存的话,sql语句就会经过分析器,分析器说白了就是要先看你的sql语句要干啥,再检查你的sql是否正确
  • 优化器: 按照mysql认为最优的方案去执行

MySql主要分为 Server 层和存储引擎层

  • server层: 主要包括连接器,查询缓存,分析器,优化器,执行器等.所有跨存储引擎的功能都在这一层实现,比如存储过程,触发器,视图,函数等,还有一个通用的日志模块binlog日志模块
  • 存储引擎: 主要负责数据的存储和读取,采用可以替换的插件式架构,支持 InnoDB,MyISAM,Memory等多个存储引擎,其中InnoDB引擎有自有的日志模块 redolog模块. 现在最常用的存储引擎是InnoDB,它从MySql 5.5.5版本开始便作为默认存储引擎了

Server层基本组件介绍

连接器

主要负责用户登录数据库,进行用户的身份认证,包括校验账户密码,权限等操作.

如果用户账户密码已通过,连接器回到权限表中查询该用户的所有权限,之后在这个连接里权限逻辑判断都是会依赖此时读取到的权限数据,也就是说,后续只要这个连接不断开,即使管理员修改了该用户的权限,用户也不会受到影响

查询缓存(MySql 8.0版本后移除)

连接建立后,执行查询语句的时候,会先查询缓存,MySql会先校验这个sql是否执行过,以key-value的形式缓存在内存中,key是查询预计,value是结果集,如果缓存key被命中,就会直接返回给客户端,如果没有命中,就会执行后续的操作,sql执行完后也会把结果缓存起来,方便下一次调用.当然,在执行缓存查询的时候还是会校验用户的权限,是否有该表的查询权限

MySQL 查询不建议使用缓存,因为查询缓存失效在实际业务场景中可能会非常频繁,假如你对一个表更新的话,这个表上的所有的查询缓存都会被清空。对于不经常更新的数据来说,使用缓存还是可以的。

所以,一般在大多数情况下我们都是不推荐去使用查询缓存的。

MySQL 8.0 版本后删除了缓存的功能,官方也是认为该功能在实际的应用场景比较少,所以干脆直接删掉了。

分析器

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

  1. 词法分析,一条sql语句由多个字符串组成,首先要提取关键字,比如select,提出查询的表,提出字段名,提出查询条件等等,做完这些操作后,就会进入第二步
  2. 语法分析, 主要就是判断你输入的sql是否正确,是否符合MySql的语法

完成这两步之后,MySql就准备开始执行了,但是如何执行,怎么执行是最好的结果呢,就需要优化器上场了

优化器

优化器的作用就是它人为的最优的执行方案去执行,比如多个索引的时候该如何选择,多表查询的时候如何选择关联顺序等

执行器

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

存储引擎层

主要负责数据的存储和提取,是基于插件的形式的架构,支持InnoDB,MyISAM等存储引擎;5.5.5后默认使用InnoDB作为默认的存储引擎。在创建表的时候不指定引擎,默认就是InnoDB,也可以通过engine=memory来指定使用的存储引擎。

InnoDB

MySQL5.5.5版本后的默认存储引擎,具体特点如下:

  • 支持事务,在可重复读和读提交隔离级别下通过MVCC一致性视图解决了不可重复读问题,通过间隙锁解决了幻读的问题。
  • 默认支持行锁,更小的锁力度支持更大的并发
  • InnoDB使用B+Tree来作为索引的数据结构,查询效率高,并在基础B+Tree上进行了优化,叶子节点通过指针相连接,提高范围查询效率
  • 数据与主键索引放在一起,叶子节点会冗余非叶子节点的数据,在叶子节点上存储主键对应的整行数据

MyISAM

MySQL5.1版本前默认的存储引擎,具体特点如下:

  • 不支持事务,不具有ACID特性
  • update时通过表锁保证数据的安全性(写操作会阻塞 读和写,读操作不会阻塞读)
  • 读数据速度快,占用资源更少,并且会记录表中记录行数
  • 不支持外键约束

语句分析

sql语句主要分两种.一种是查询,一种是更新(新增,编辑,删除)

查询语句

  • 对于查询语句,它的执行流程如下

    1
    select * from student A where A.age='18' and A.name='张三';
    • 先检查该语句是否有权限,如果没有权限,直接返回错误信息,如果有权限,在MySql8.0版本之前,会先检查缓存,以这条sql语句为key在内存中查询是否有结果,如果有直接返回,如果没有,则执行下一步

    • 通过分析器进行词法分析,提取sql语句的关键元素,比如提取上面这个语句是select查询语句,提取需要查询的表名为 student,需要查询所有的列,然后会判断这个sql语句是否有语法错误,比如关键字是否正确等等,如果检查没有问题执行下一步

    • 接下来就是优化器精选确定执行方案,上面的SQL语句,可以有两种执行方案,

      • a.先查询student表中name为”张三”的学生,然后判断age是否为”18”
      • b.先找出student表中age为”18”的学生,再查询name是”张三”的学生

      那么优化器根据自己的优化算法进行选择执行效率最好的一个方案,那么确定了执行计划后就准备开始执行了

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

更新语句

  • 对于更新语句,他的执行流程如下

    1
    update student A set A.age='19' where A.name=' 张三 ';

    其实,更新语句也基本是按照查询语句的流程走,只不过在执行更新的时候哟啊记录日志,这就会引入日志模块了,MySql自带的日志模块是binlog(归档日志),所有的存储引擎都可以使用,我们常用的InnoDB引擎还自带了一个日志模块redolog(重做日志),我们就以InnoDB模式在探讨一下这个SQL语句的执行流程.流程如下

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

    为什么要用两个日志模块,用一个不可以嘛?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    这是因为最开始 MySQL 并没有 InnoDB 引擎(InnoDB 引擎是其他公司以插件形式插入 MySQL 的),MySQL 自带的引擎是 MyISAM,但是我们知道 redo log 是 InnoDB 引擎特有的,其他存储引擎都没有,这就导致会没有 crash-safe 的能力(crash-safe 的能力即使数据库发生异常重启,之前提交的记录都不会丢失),binlog 日志只能用来归档。

    > 先写 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 的处理过程如下:
    1.判断 redo log 是否完整,如果判断是完整的,就立即提交。
    2.如果 redo log 只是预提交但不是 commit 状态,这个时候就会去判断 binlog 是否完整,如果完整就提交 redo log, 不完整就回滚事务。