转载自: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. hadoop过程中遇到的错误与解决方法

    本文整理了在hadoop学习过程中遇到的各种问题. windows下开发环境搭建 大部分情况下,我们都是在windows下开发,hadoop则一般部署于linux服务器(无论是CDH还是原生hadoo ...

  2. Python3学习笔记----生成器

    生成器的作用 生成器可以在循环的过程中推算出后续的元素,不必创建完整的list,从而节省大量空间. 生成器的创建 1.把一个列表生成式的 [ ] 改写为( ) # -*- coding: UTF-8 ...

  3. python-文字转语音-pyttsx3

    pyttsx3 python 文字转语音库,支持英文,中文,可以调节语速.语调等. 安装 pip install pyttsx3 示例 import pyttsx3 teacher = pyttsx3 ...

  4. 通过java代码往mysql数据库中写入日期相关数据少13个小时

    通过show variables like '%time_zone%'; 查看时区: CST 时区 名为 CST 的时区是一个很混乱的时区,有四种含义: 美国中部时间 Central Standard ...

  5. Python3爬虫相关软件,库的安装

    Anaconda 百度搜Anaconda清华,根据环境选择版本下载 安装时记得勾选添加到环境变量,不要还要手动添加 Anaconda Navigator可视化界面,可以方便地调用Jupyter等工具. ...

  6. C#中得到程序当前工作目录和执行目录的五种方法

    string str="";str += "\r\n" + System.Diagnostics.Process.GetCurrentProcess().Mai ...

  7. 【安卓基础】ImageView与EditText联动实现隐藏与显示密码

    项目中经常会有这样的需求,在密码输入框的右边有一个小图标,点击就切换显示和隐藏密码. 其实这里需求实现起来是比较容易的,主要考虑是复用问题,因为登陆.注册.修改密码界面都会有这样的情景,如果每个界面都 ...

  8. Cocos Creator学习三:生命周期回调函数

    1.目的:学习生命周期回调函数以及回调顺序,更有利于我们逻辑的处理把控. 2.生命周期回调函数: 节点:指cc.Node:组件:指cc.Component. ①onLoad:脚本组件绑定的节点所在场景 ...

  9. MongoDB数据查询 --MongoDB

    1.插入测试数据 use flower db.goods.insert({'goods_name':'Hyacinth',price:10,num:800}) db.goods.insert({goo ...

  10. vue单位文本控件与vue加密文本控件

    vue单位文本控件: 使用方式: npm install dami-text-input --save 使用: <text-input v-model="test" :uni ...