转载自:http://www.cnblogs.com/bamboos/p/3553703.html?utm_source=tuicool&utm_medium=referral

两次写可以说是在Innodb中很独特的一个功能点,而关于它的说明或者解释非常少,至于它存在的原因更没有多少文章来说,所以我打算专门对它做一次说明。

首先说明一下为什么会有两次写这个东西:
因为innodb中的日志是逻辑的,所谓逻辑就是比如当插入一条记录时,它可能会导致在某一个页面(这条记录最终被插入的位置)的多个偏移位置写入某个长度的值,比如页头的记录数,槽数,页尾槽数据,页中的记录值等等,这些本是一些物理操作,而innodb为了节约日志量及其它一些原因,设计为逻辑处理的方式,那就是它会在一个页面的基础上,把一条记录插入,那么在日志记录中记录的内容为表空间号、页面号、记录的各个列的值等等,在内部转换为上面的物理操作。

但这里的一个问题是,如果那个页面本身是错误的,这种错误有可能是因为写断裂(1个页面为16K,分多次写入,后面的有可能没有写成功,导致这个页面不完整)引起的,那么这个逻辑操作就没办法完成了,因为它的前提是这个页面还是正确的,完整的,因为如果这个页面不正确的话,这个页面里的数据是无效的,有可能产生各种不可预料的问题。

那么正是因为这个问题,所以必须要首先保证这个页面是正确的,方法就是两次写,它的思想最终是一种备份思想,也就是一种镜像。

下面就它的实现方式说明一下:

首先是它的创建,在初始化一个库的时候,系统会在系统页面5号页面的尾部(大约是16K-200字节的位置)初始化所有关于两次写的信息,这些信息包括:

----------------------------------------------------------------
#define TRX_SYS_DOUBLEWRITE_FSEG 0 /*这里存储的是两次写页面所在的段的地址信息 */
#define TRX_SYS_DOUBLEWRITE_MAGIC FSEG_HEADER_SIZE
/*!< 用来判断是不是已经初始化过两次写页面 */
#define TRX_SYS_DOUBLEWRITE_BLOCK1 (4 + FSEG_HEADER_SIZE)
/*!两次写页面的第一个簇的首地址,两次写页面总共有2个簇,一个簇为64个页面*/
#define TRX_SYS_DOUBLEWRITE_BLOCK2 (8 + FSEG_HEADER_SIZE)
/*!第二个簇的首地址*/
#define TRX_SYS_DOUBLEWRITE_REPEAT 12 /*!< 将上面的MAGCI、BLOCK1与BLOCK2重复存储,防止自己的不完整*/
----------------------------------------------------------------

两次写总共包括2M(默认值)的数据,有2个BLOCK,那么每一个BLOCK是1M,每个页面是16K,那么一个BLOCK包括64个页面,正是一个簇的大小。,所以其实两次写页面的空间是2个簇的空间。

那么初始化所要做的工作就是将上面的信息补充完整,BLOCK1与BLOCK2分别对应2个簇的首地址,同时还要申请2个簇的内存空间,用来缓存这些数据。

除上面的信息之外,还会有一个空间用来存储这128个页面的页面信息,是用来在刷两次写页面之后,要做对应的页面刷盘操作,这是一个长度为128的数组。

有了上面的信息之后,则两次写初始化完成。

下面说明一下它的使用过程:
在做页面刷盘的时候,如果开启了两次写的功能,则innodb要做的不是简单的直接将数据做io操作写入到硬盘,而是先将当前要写的页面按照顺序拷贝到两次写内存缓存空间中去,上面已经说了它的大小为128个页面的大小,同时要在页面数组中对应的将页面的地址记录下来,然后就算刷盘完成了,但实际上,此时要写出的页面都在两次写的内存缓存空间中的。

当缓存空间满了的时候,上面的操作会触发真正的刷盘操作,两次写的写入是,首先判断当前缓存中有多少个簇,也就是说判断BLOCK1中有没有数据,如果没有数据则直接不写了,如果有则写入,以这个簇实际大小写入,然后再判断BLOCK2中有没有数据,然后做同样的处理。
在写完两次写缓存中的数据之后,然后再将页面数组中的每一个页面按照顺序从前到后再一个一个的将其刷入到文件中,此时,才真正的将这些页面刷盘完成。

当然两次写缓存写出硬盘不只是上面一个机会,其它刷盘的操作也会触发这个操作,那时可能缓存中的页面数还没有达到128个。

上面已经说完了两次写的写入及初始化,最后说一下它是如何起作用的:

在数据库启动时(异常关闭的情况下),都会做数据库恢复(redo)操作,恢复的过程中,数据库都会检查页面是不是合法(校验等等),如果发现一个页面校验结果不一致,则此时会用到两次写这个功能,这个特点也正是为了处理这样的错误而设计的。

此时的操作很明白了,将两次写的2个BLOCK(簇)都读出来,然后将所有这些页面写回到对应的页面中去,那么这时可以保证这些页面是正确的,并且是在写入前已经更新过的(最新数据)。
在写回对应页面中去之后,那么就可以在这基础上继续做数据库恢复了,之后则不会再遇到这样的问题了,因为已经将最后有可能产生写断裂的数据页面都恢复了。

问题:
上面说的都是数据页面有问题的情况下可以通过两次写页面来恢复,但是如果2次写页面本身发生写断裂怎么办?
对于这个问题,其实是不用担心的,因为如果两次写有问题,则本身数据页面就没有做写操作,此时系统挂了,发生错误的是两次写页面,而数据页面在挂之前都是在buffer里面,文件中还是当前事务操作前的值,它自己没有变,还是一致状态,所以两次写页面压根就不会被使用到。

总结:

1. 两次写在任何时候记录的都是数据库最后发生改变的若干页面(最多128个),在数据库不断工作的过程中,它会不断的被覆盖,它始终是最新的数据,记录的是修改之后的页面数据,而不是修改之前的数据,它的作用不是还原数据,而是保证不会丢失修改。
2. 至于性能问题,表面看上去,它是每一个页面都写了2遍,则会非常影响性能,但实际上,由于将所写的页面都先缓存到内存中,到达128个之后才真正写入,那么对于磁盘而言,连续写与分散写(每个页面自己写)的性能相差很大的,而两次写正是将一个簇数量的页面组合起来形成2个连续的空间写入到两次写空间中,有效的利用这了这特点,所以性能是不会相差1倍的。实际上经过测试,可能两次写使得性能降低了10%。 
3. 有其它一些数据库完全没有类似2次写的问题,比如达梦等,这个主要是由于它们采用了全物理的REDO,将一个页面的写操作都拆成一个个的小的物理写入,这种情况下就不会存在写断裂的情况,因为不管怎么写,日志都是对一个页面操作的重演,在REDO做完之后,页面的状态肯定是正确的。

Mysql 源码:关于innodb中两次写的探索的更多相关文章

  1. mysql源码:关于innodb中两次写的探索

    两次写可以说是在Innodb中很独特的一个功能点,而关于它的说明或者解释非常少,至于它存在的原因更没有多少文章来说,所以我打算专门对它做一次说明. 首先说明一下为什么会有两次写这个东西:因为innod ...

  2. Netty源码之解码中两种数据积累器(Cumulator)的区别

    上一篇随笔中已经介绍了解码核心工作流程,里面有个数据积累器的存在(Cumulator),其实解码中有两种Cumulator,那他们的区别是什么呢? 还是先打开ByteToMessageDecoder的 ...

  3. ubuntu中Eclipse-cpp编译MySQL源码

    1.下载eclipse-cpp-mars-2-linux-gtk-x86_64.tar.gz压缩包,在Ubuntu中解压,运行. 此处有可能会出现jdk错误,只要原因是因为jdk没安装或者jdk版本太 ...

  4. MySQL源码分析以及目录结构 2

    原文地址:MySQL源码分析以及目录结构作者:jacky民工 主要模块及数据流经过多年的发展,mysql的主要模块已经稳定,基本不会有大的修改.本文将对MySQL的整体架构及重要目录进行讲述. 源码结 ...

  5. MySQL源码分析以及目录结构

    原文地址:MySQL源码分析以及目录结构作者:jacky民工 主要模块及数据流经过多年的发展,mysql的主要模块已经稳定,基本不会有大的修改.本文将对MySQL的整体架构及重要目录进行讲述. 源码结 ...

  6. 深入MySQL源码 学习方法 何登成专家

    MYSQL 技术圈 有哪些做得好,又注重分享的公司: Oracle MySQL, MariaDB, Percona,Google, FB, Twitter, Taobao, NetEase… 有哪些值 ...

  7. [转]MySQL源码:Range和Ref优化的成本评估

    MySQL源码:Range和Ref优化的成本评估 原文链接:http://www.orczhou.com/index.php/2012/12/mysql-source-code-optimizer-r ...

  8. Dubbo入门到精通学习笔记(十九):MySQL源码编译安装、MySQL主从复制的配置

    文章目录 MySQL 源码编译安装(CentOS-6.6+MySQL-5.6) 一.服务器配置: 二.源码安装 MySQL5.6.26: MySQL主从复制的配置 环境 依赖课程 MySQL 主从复制 ...

  9. mysql源码分析-启动过程

    mysql源码分析-启动过程 概要 # sql/mysqld.cc, 不包含psi的初始化过程 mysqld_main: // 加载my.cnf和my.cnf.d,还有命令行参数 if (load_d ...

随机推荐

  1. 泛型集合List的详细用法

    命名空间:   System.Collections.Generic List<T>类是 ArrayList 类的泛型等效类.    该类使用大小可 按需动态增加 的数组实现 IList& ...

  2. CentOS 离线安装 MYSQL+APACHE+PHP

    一.MYSQL安装 下载MYSQL安装包:MySQL-client-XXX.rpm   MySQL-server-XXX.rpm   MySQL-devel-XXX.rpm 如有冲突,要先删除原来的M ...

  3. spring-petclinic性能调优实战(转)

    1.spring-petclinic介绍 spring-petclinic是spring官方做的一个宠物商店,结合了spring和其他一些框架的最佳实践. 架构如下: 1)前端 Thymeleaf做H ...

  4. 读 vue 源码一 (为什么this.message能够访问data里面的message)

    12月离职后,打算在年后再找工作了,最近陆陆续续的看了黄轶老师的vue源码解析,趁着还有几天过年时间记录一下. 目标:vue如何实现通过this.key,就能直接访问data,props,method ...

  5. python学习之——习题一

    习题一:使用while循环输入1 2 3 4 5 6   8 9 10 (不含7) 首先想到,先使用while循环打印出1-10数字,然后再将数字“7”剔除. # 先打印出1-10 n = 1 whi ...

  6. [Java]如何制作一个WordCount-Plus的Plugin

    主类 每个Plugin都有一个主类实现了com.jihuayu.wordcount.api.Plugin接口,这个主类就是插件的路口. 获取命令介绍 可以通过向方法getCommandUsage的参数 ...

  7. Clover 安装 Mac 系统更新 (原版黑苹果)

    关于使用原版镜像(即 .dmg )安装黑苹果的升级,笔者写写自身经验吧. 在Clover启动的界面中与Mac OS有关的启动菜单有以下这些: Boot FileVault Prebooter from ...

  8. IOS 模块并且发布到NPM

      注释:导入出错 请使用这个 #import <React/RCTBridge.h>   参考文档:http://www.liuchungui.com/blog/2016/05/02/r ...

  9. Jmeter接口测试实例

    此文章作为工作中用到的jmeter接口测试相关内容简述,方便日后查阅参考,如有理解描述有误之处,欢迎指出. 首先Jmeter环境准备网上有很多教程,在此不多做赘述: 1.接口简述 接口可理解为从客户端 ...

  10. Hybrid App 开发模式

    开发移动App主要有三种模式:Native. Hybrid 和 Web App. 需要注意的一点是在选择开发模式的时候,要根据你的项目类型(图片类?视频类?新闻类?等),产品业务和人员技术储备等做权衡 ...