Mysql从入门到入神之(二)Select 和Update的执行过程

前言

文本已收录至个人GitHub仓库,欢迎Star:github.com/bin39232820…
种一棵树最好的时间是十年前,其次是如今
我知道不少人不玩qq了,可是怀旧一下,欢迎加入六脉神剑Java菜鸟学习群,群聊号码:549684836 鼓励你们在技术的路上写博客mysql

絮叨

咱们继续来探索mysql。上一篇文章git

一条Select语句的查询过程

大致来讲,MySQL 能够分为 Server 层和存储引擎层两部分。github

Server 层包括链接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服 务功能,以及全部的内置函数(如日期、时间、数学和加密函数等),全部跨存储引擎的功能都 在这一层实现,好比存储过程、触发器、视图等。redis

而存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、 Memory 等多个存储引擎。如今最经常使用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始成 为了默认存储引擎。sql

也就是说,你执行 create table 建表的时候,若是不指定引擎类型,默认使用的就是 InnoDB。不过,你也能够经过指定存储引擎的类型来选择别的引擎数据库

链接器

链接器 很好理解就是你输入帐号密码去链接mysql ,好比咱们navicat客户端,好比咱们JDBC数据库链接池这些,都算是链接缓存

链接完成后,若是你没有后续的动做,这个链接就处于空闲状态,你能够在 show processlist 命令中看到它。文本中这个图是 show processlist 的结果,其中的 Command 列显示 为“Sleep”的这一行,就表示如今系统里面有一个空闲链接架构

创建链接的过程一般是比较复杂的,在使用中要尽可能减小创建链接的动做,也就是尽可能使用长链接。因此咱们通常用池技术咯

查询缓存

链接创建完成后,你就能够执行 select 语句了。执行逻辑就会来到第二步:查询缓存。函数

MySQL 拿到一个查询请求后,会先到查询缓存看看,以前是否是执行过这条语句,也就是相似于redis key 就是你的sql value 就算sql的返回值。post

可是大多数状况下我会建议你不要使用查询缓存,为何呢?由于查询缓存每每弊大于利。

举个例子 假设你这个sql是查当前的订单列表查询,每次只要有人插入订单,那么你这个缓存就要被删除,因此说这样的话,对于跟新数据库还要多一步操做,因此是没有必须的,mysql默认是关闭缓存的,而且在mysql 8中是没有缓存这个概念了。

你能够将参数 query_cache_type 设置成 DEMAND,这样对于默认的 SQL 语句都不使用查询缓存

分析器

若是没有命中查询缓存,就要开始真正执行语句了。首先,MySQL 须要知道你要作什么,所以 须要对 SQL 语句作解析

分析器先会作“词法分析”。你输入的是由多个字符串和空格组成的一条 SQL 语句,MySQL 须要识别出里面的字符串分别是什么,表明什么。

优化器

通过了分析器,MySQL 就知道你要作什么了。在开始执行以前,还要先通过优化器的处理。

优化器是在表里面有多个索引的时候,决定使用哪一个索引

执行器

MySQL 经过分析器知道了你要作什么,经过优化器知道了该怎么作,因而就进入了执行器阶 段,开始执行语句

对于有索引的表,执行的逻辑也差很少。第一次调用的是“取知足条件的第一行”这个接口,之 后循环取“知足条件的下一行”这个接口,这些接口都是引擎中已经定义好的

小问题

若是表 T 中没有字段 k,而你执行了这个语句 select * from T where k=1, 那确定是会报“不存在这个列”的错误: “Unknown column ‘k’ in ‘where clause’”。你以为这个错误是在咱们上面提到的哪一个阶段报出来的呢?

应该是分析器。优化器是对sql语句优化出一个最好的方案,若是没有该表,也就不存 在优化一说,因此应该是在分析器里面对sql作了分析以后,发现没有该表就直接抛异常提示 了。

SQL更新语句是如何执行的?

前面咱们系统了解了一个查询语句的执行流程,并介绍了执行过程当中涉及的处理模块。相信你还 记得,一条查询语句的执行过程通常是通过链接器、分析器、优化器、执行器等功能模块,最后 到达存储引擎

那么,一条更新语句的执行流程又是怎样的呢?

能够肯定的说,查询语句的那一套流程,更新语句也是一样会走一遍

你执行语句前要先链接数据库,这是链接器的工做。

在一个表上有更新的时候,跟这个表有关的查询缓存会失效,因此这条语句就会 把表 T 上全部缓存结果都清空。这也就是咱们通常不建议使用查询缓存的缘由

接下来,分析器会经过词法和语法解析知道这是一条更新语句。优化器决定要使用 ID 这个索 引。而后,执行器负责具体执行,找到这一行,而后更新。

与查询流程不同的是,更新流程还涉及两个重要的日志模块,它们正是咱们今天要讨论的主 角:redo log(重作日志)和 binlog(归档日志)

重要的日志模块:redo log

不知道你还记不记得《孔乙己》这篇文章,酒店掌柜有一个粉板,专门用来记录客人的赊帐记 录。若是赊帐的人很少,那么他能够把顾客名和帐目写在板上。但若是赊帐的人多了,粉板总会 有记不下的时候,这个时候掌柜必定还有一个专门记录赊帐的帐本。

若是有人要赊帐或者还帐的话,掌柜通常有两种作法

  • 一种作法是直接把帐本翻出来,把此次赊的帐加上去或者扣除掉
  • 另外一种作法是先在粉板上记下此次的帐,等打烊之后再把帐本翻出来核算。

在生意红火柜台很忙时,掌柜必定会选择后者,由于前者操做实在是太麻烦了。首先,你得找到 这我的的赊帐总额那条记录。你想一想,密密麻麻几十页,掌柜要找到那个名字,可能还得带上老 花镜慢慢找,找到以后再拿出算盘计算,最后再将结果写回到帐本上。

在 MySQL 里也有这个问题,若是每一次的更新操做都须要写进磁盘,而后磁盘也要找到 对应的那条记录,而后再更新,整个过程 IO 成本、查找成本都很高。为了解决这个问题, MySQL 的设计者就用了相似酒店掌柜粉板的思路来提高更新效率

而粉板和帐本配合的整个过程,其实就是 MySQL 里常常说到的 WAL 技术,WAL 的全称是 Write-Ahead Logging,它的关键点就是先写日志,再写磁盘,也就是先写粉板,等不忙的时 候再写帐本

具体来讲,当有一条记录须要更新的时候,InnoDB 引擎就会先把记录写到 redo log(粉板) 里面,并更新内存,这个时候更新就算完成了。同时,InnoDB 引擎会在适当的时候,将这个操 做记录更新到磁盘里面,而这个更新每每是在系统比较空闲的时候作,这就像打烊之后掌柜作的 事。

与此相似,InnoDB 的 redo log 是固定大小的,好比能够配置为一组 4 个文件,每一个文件的大 小是 1GB,那么这块“粉板”总共就能够记录 4GB 的操做。从头开始写,写到末尾就又回到开 头循环写,以下面这个图所示。

wirte pos 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。 checkpoint 是当前要擦除的位置,也是日后推移而且循环的,擦除记录前要把记录更新到数据 文件。

write pos 和 checkpoint 之间的是“粉板”上还空着的部分,能够用来记录新的操做。若是 write pos 追上 checkpoint,表示“粉板”满了,这时候不能再执行新的更新,得停下来先擦 掉一些记录,把 checkpoint 推动一下。

有了 redo log,InnoDB 就能够保证即便数据库发生异常重启,以前提交的记录都不会丢失, 这个能力称为crash-safe。

重要的日志模块:binlog

MySQL 总体来看,其实就有两块:一块是 Server 层,它主要作的是 MySQL 功能层面的事情;还有一块是引擎层,负责存储相关的具体事宜。上面咱们聊到的粉板 redo log 是 InnoDB 引擎特有的日志,而 Server 层也有本身的日志,称为 binlog(归档日志)。

为何会有两份日志呢?

由于最开始 MySQL 里并无 InnoDB 引擎。MySQL 自带的引擎是 MyISAM,可是 MyISAM 没有 crash-safe 的能力,binlog 日志只能用于归档。而 InnoDB 是另外一个公司以插件形式引入 MySQL 的,既然只依靠 binlog 是没有 crash-safe 能力的,因此 InnoDB 使用另一套日志系 统——也就是 redo log 来实现 crash-safe 能力。

redo log 和binlog的区别

  • redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,全部引擎均可 以使用
  • redo log 是物理日志,记录的是“在某个数据页上作了什么修改”;binlog 是逻辑日志, 记录的是这个语句的原始逻辑,好比“给 ID=2 这一行的 c 字段加 1
  • redo log 是循环写的,空间固定会用完;binlog 是能够追加写入的。“追加写”是指 binlog 文件写到必定大小后会切换到下一个,并不会覆盖之前的日志。

真正的执行流程

update T set c=c+1 where ID=2;

  • 执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。若是 ID=2 这一行所在的数据页原本就在内存中,就直接返回给执行器;不然,须要先从磁盘读入内 存,而后再返回。
  • 执行器拿到引擎给的行数据,把这个值加上 1,好比原来是 N,如今就是 N+1,获得新的 一行数据,再调用引擎接口写入这行新数据。
  • 引擎将这行新数据更新到内存中,同时将这个更新操做记录到 redo log 里面,此时 redo log 处于 prepare 状态。而后告知执行器执行完成了,随时能够提交事务
  • 执行器生成这个操做的 binlog,并把 binlog 写入磁盘。
  • 执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改为提交(commit)状 态,更新完成。

能够看到,若是不使用“两阶段提交”,那么数据库的状态就有可能和用它的日志恢复出来的库 的状态不一致。

简单说,redo log 和 binlog 均可以用于表示事务的提交状态,而两阶段提交就是让这两个状态 保持逻辑上的一致。

结尾

文章出自极客时间的Mysql45讲,很是不错哦

平常求赞

好了各位,以上就是这篇文章的所有内容了,能看到这里的人呀,都是真粉

创做不易,各位的支持和承认,就是我创做的最大动力,咱们下篇文章见

六脉神剑 | 文 【原创】若是本篇博客有任何错误,请批评指教,不胜感激 !

相关文章
相关标签/搜索