MySQL-简单了解InnoDB底层原理

文章内容一部分来自 : https://mp.weixin.qq.com/s/-puz311svMVbBAdRioPrnQ , 一部分是自己的写的

MySQL 组织架构

下面这张图就可以解释关于 MySQL 底层架构了。

1297993-20200105230617789-283062258.png

上面的图可以直观地展示两个重要的东西 :

  • 一条 SQL 的执行流程
  • MySQL 的底层架构

大体来说,MySQL可以分为Server层和存储引擎层两部分。

Server 层

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

存储引擎层

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

也就是说,你执行create table建表的时候,如果不指定引擎类型,默认使用的就InnoDB。不过,你也可以通过指定存储引擎的类型来选择别的引擎,比如在create table语句中使用engine=memory, 来指定使用内存引擎创建表。

不同存储引擎的表数据存取方式不同,支持的功能也不同,在后面的文章中,我们会讨论到引擎的选择。

执行流程

mysql> create table T(ID int primary key, c int);

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

有了对这两个日志的概念性理解,我们再来看执行器和InnoDB引擎在执行这个简单的update语句时的内部流程。

  1. 执行器先找引擎取ID=2这一行。ID是主键,引擎直接用树搜索找到这一行。如果ID=2这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。

  2. 执行器拿到引擎给的行数据,把这个值加上1,比如原来是N,现在就是N+1,得到新的一行数据,再调用引擎接口写入这行新数据。

  3. 引擎将这行新数据更新到内存中,同时将这个更新操作记录到redo log里面,此时redo log处于prepare状态。然后告知执行器执行完成了,随时可以提交事务。

  4. 执行器生成这个操作的binlog,并把binlog写入磁盘。

  5. 执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交(commit)状态,更新完成。

这里我给出这个update语句的执行流程图,图中浅色框表示是在InnoDB内部执行的,深色框表示是在执行器中执行的。

1297993-20200105230343948-298161416.png

下面这张图就很好得解释了这个过程 .

img

buffer pool 也就是数据缓存 !!

redo-log 和 bin-log

这两个log 日志文件主要是用作持久化功能的,那么它们各自恢复什么数据呢?并且这两者之间有什么差异呢?
例子 :

一个古代的酒店,掌柜有个账本,客人来吃饭需要记账,但是有时客人很多,来不得及一下子全部记下去,并且有个问题,例如张三今天过来消费,那么掌柜需要先找到账本中张三的总账,然后再在上面加上一笔,
那样在客人多的时候根本来不得处理,于是掌柜就拿出了小黑板,当客人多的时候就先往小黑板上记录,然后闲的时候再从小黑板移入账本.

但是小黑板的空间毕竟有限,那么当写不下的时候掌柜就先把小黑板的数据搬入到账本中,然后小黑板的数据擦掉,可以让小黑板继续记账。

redo-log

上面例子中的小黑板就是 redo-log ,而 bin-log 就是账本。我们刚才讲到小黑板有满的时候,那么“满了”的临界点在redo-log 中就是 checkpoint 。InnoDB的redo log是固定大小的,比如可以配置为一组4个文件,每个文件的大小是1GB,那么这块“粉板”总共就可以记录4GB的操作。从头开始写,写到末尾就又回到开头循环写,如下面这个图所示。

1297993-20200105230532815-1103762742.jpg

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

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

有了redo log,InnoDB就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为crash-safe。例如MySQL在提交事务的时候,提交了一半,服务器就down 了,那么 redo-log 依旧还保持着这条还未提交的事务。

bin-log

前面我们讲过,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能力。

两个日志的区别

  1. redo log是InnoDB引擎特有的;binlog是MySQL的Server层实现的,所有引擎都可以使用。
  2. redo log是物理日志(结果日志),记录的是“在某个数据页上做了什么修改”(n页k改为5 ,p页k1改为 6 ,w页k2 改为 7 );
  3. binlog是逻辑日志(过程日志),记录的是这个语句的原始逻辑,比如“给ID=2这一行的c字段加1 ”(k = 2 ,k= k+1 , k= k*k )。
  4. redo log是循环写的,空间固定会用完;binlog是可以追加写入的。“追加写”是指binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。

两阶段提交

两阶段提交 , 很熟 ,是分布式事务的范畴了, 为了保证两个事务的一致性 , 例如 A 往 B 转钱 , 一个会扣钱, 一个将会到账 , 要不就同时成功 , 要不就转账失败, 对方也不会到账 , 如果出现转账失败 ,而对方又到账成功 ,那么数据就不一致了.

redo log 和 bin log 之间的日志写入到磁盘也是使用了两阶段提交过程 ,具体的过程是这样的.

img

  • Prepare阶段,将Redo Log写入文件,并刷入磁盘,记录上内部XA事务的ID,同时将Redo Log状态设置为Prepare。Redo Log写入成功后,再将Binlog同样刷入磁盘,记录XA事务ID。

  • Commit阶段,向磁盘中的Redo Log写入Commit标识,表示事务提交。然后执行器调用存储引擎的接口提交事务。这就是整个过程。

下面这段来自 : https://zhuanlan.zhihu.com/p/346970015

验证2PC机制的可用性

这就是2PC提交Redo Log和Binlog的过程,那在这个期间发生了异常,2PC这套机制真的能保证数据一致性吗?

假设Redo Log刷入成功了,但是还没来得及刷入Binlog MySQL就挂了。此时重启之后会发现Redo Log并没有Commit标识,此时根据记录的XA事务找到这个事务,进行回滚。

如果Redo Log刷入成功,而且Binlog也刷入成功了,但是还没有来得及将Redo Log从Prepare改成Commit MySQL就挂了,此时重启会发现虽然Redo Log没有Commit标识,但是通过XID查询到的Binlog却已经成功刷入磁盘了。

此时,虽然Redo Log没有Commit标识,MySQL也要提交这个事务。因为Binlog一旦写入,就可能会被从库或者任何消费Binlog的消费者给消费。如果此时MySQL不提交事务,则可能造成数据不一致。而且目前Redo Log和Binlog从数据层面上,其实已经Ready了,只是差个标志位。

MySQL 的 master 线程执行事件

MySQL 的 master 线程执行事件如下 :
图例

参考资料

posted @ 2020-01-05 23:21  float123  阅读(255)  评论(0编辑  收藏  举报