世界上最快的捷径,就是脚踏实地,本文已收录【架构技术专栏】关注这个喜欢分享的地方。

前序

InnoDB引擎有几个重点特性,为其带来了更好的性能和可靠性:

  • 插入缓冲(Insert Buffer)

  • 两次写(Double Write)

  • 自适应哈希索引(Adaptive Hash Index)

  • 异步IO(Async IO)

  • 刷新邻接页(Flush Neighbor Page)

今天我们的主题就是 两次写(Double Write), 先一句话概括下:

上一次我们讲过Insert Buffer 是用来提高存储引擎性能上的提升,Double Write 就是为了在数据库崩溃恢复时保证数据不丢失的一个重要特性,保证了数据的可靠性。

概念点

如图,还是先来说几个基础的概念:

  • 数据库表空间由段(segment)、区(extent)、页(page)组成

  • 默认情况下有一个共享表空间ibdata1,如使用了innodb_file_per_table则每张表独立表空间(指存放数据、索引、插入缓冲bitmap页)

  • 段包括了数据段(B+树的叶子结点)、索引段、回滚段

  • 区,由连续的页组成,任何情况下每个区都为1M,一个区中有64个连续页(16k)

  • 页,数据页(B-tree Node)默认大小为16KB

  • 文件系统一页 默认大小为4KB

  • 盘片被分为许多扇形的区域,每个区域叫一个扇区,硬盘中每个扇区的大小固定为512字节

  • 脏页,当数据从磁盘加载到缓冲池的数据页后,数据页内容被修改后,此数据页称为脏页

出现的问题

通过上次讲的 重要,知识点:InnoDB的插入缓冲 我们知道,脏页会在某些场景下进行刷盘,将缓冲池内的脏页数据落地到磁盘。

因为存储引擎缓冲池内的数据页大小默认为16KB,而文件系统一页大小为4KB,所以在进行刷盘操作时,就有可能发生如下场景:

如图所示,数据库准备刷新脏页时,需要四次IO才能将16KB的数据页刷入磁盘。

但当执行完第二次IO时,数据库发生意外宕机,导致此时才刷了2个文件系统里的页,这种情况被称为写失效(partial page write)。

此时重启后,磁盘上就是不完整的数据页,就算使用redo log也是无法进行恢复的。

注意

  • redo log无法恢复数据页损坏的问题,恢复必须是数据页正常并且redo log正常。

  • 这里要知道一点,redo log中记录的是对页的物理操作,如偏移量600,写'xxxx'记录。

  • 如果这个页本身已经发生了损坏,再对其进行重做是没有意义的

该怎么解决这个问题

那应该怎么来解决这个问题呢?其实大家想一下就会有个大概的答案,就是给它搞个备份呗。

如果写脏页的时候发生宕机,在重启后使用下备份先恢复下数据页在写磁盘就可以了,其实这就是Double Write

Double Write 出现

千呼万唤始出来,为了防止我们可怜的数据被破坏,InnoDB存储引擎提供了重要的Double Write 特性,避免了数据丢失的惨剧发生。

下面我们来慢慢的来看看Double Write 到底是怎么提高可靠性的

Double Write 解决的问题

在数据库进行脏页刷新时,如果此时宕机,有可能会导致磁盘数据页损坏,丢失我们重要的数据。此时就算重做日志也是无法进行恢复的,因为重做日志记录的是对页的物理修改。

其实就是在重做日志前,用户需要一个页的副本,当写入失效发生时,先通过页的副本来还原该页,再进行重做,这就是double write。

Double Write 架构

如图,其实Double Write 分为了两个组成部分:

  • 内存中的double write buffer
  • 物理磁盘上共享表空间中连续的128个页,即2个区(extent),大小同样为2MB

可以看出,有了Double write后的脏页刷新流程就是多了几步操作:

  1. 在对缓冲池的脏页进行刷新时,并不直接写磁盘,而是会通过memcpy函数将脏页先复制到内存中的Double write buffer

  2. 通过Double write buffer再分两次,每次1MB顺序地写入共享表空间的物理磁盘上,然后马上调用fsync函数,同步磁盘,避免缓冲写带来的问题

Double write崩溃恢复

如图,如果操作系统在将页写入磁盘的过程中发生了崩溃,在恢复过程中,InnoDB存储引擎可以从共享表空间中的Double write中找到该页的一个副本,将其复制到表空间文件,再应用重做日志。

下面显示了一个由Double write进行恢复的情况:

090923 12:36:32 mysqld restarted
090923 12:26:33 InnoDB: Database was not shut down normally!
InnoDB: Starting crash recovery.
InnoDB: Reading tablespace information from the .ibd files...
InnoDB: Crash recovery may have faild for some .ibd files!
InnoDB: Restoring possible half-written data pages from the doublewrite.
InnoDB: buffer...

Double Write 的问题

Double write buffer 它是在物理文件上的一个buffer, 其实也就是file,所以它会导致系统有更多的fsync操作,而因为硬盘的fsync性能问题,所以也会影响到数据库的整体性能。

Double write页是连续的,因此这个过程是顺序写的,开销并不是很大。

在完成Double write页的写入后,再将Double write buffer中的页写入各个数据文件中,此时的写入则是离散的

总结

  1. 当commit 一个修改语句时,如果redo log有空闲区域,直接写redo log,如果redo log没有空闲区域,那么需要把被覆盖的redo log对应的数据页刷新到data file 中,最后改pool buffer中的记录

  2. innodb的redo log 不会记录完整的一页数据,因为这样日志太大,它只会记录那次(sequence)如何操作了(update,insert)哪页(page)的哪行(row)

  3. 因为数据库使用的页(page,默认16KB)大小和操作系统对磁盘的操作页(page,默认4KB)不一样,当提交了一个页需要刷新到磁盘,会有多次IO, 此时刷了前面的8k时异常发生宕机。在系统恢复正常后,如果没有double write机制,此时数据库磁盘内的数据页已损坏,无法使用redo log进行恢复。

  4. 如果有double write buffer,会检查double writer的数据的完整性,如果不完整直接丢弃double write buffer内容,重新执行那条redo log,如果double write buffer的数据是完整的,用double writer buffer的数据更新该数据页,跳过该redo log。

最新文章

  1. Ruby Gem命令详解
  2. ROWNUMBER() OVER( PARTITION BY COL1 ORDER BY COL2)用法,先分组,然后在组内排名,分组计算,主表与附表一对多取唯一等
  3. STM32 C语言,端口映射
  4. AVL树(一)之 图文解析 和 C语言的实现
  5. css3实现动画效果
  6. 叠罗汉III之推箱子
  7. Cocos2d-JS v3.0 alpha 导入 cocostudio的ui配置
  8. MongoDB - Introduction to MongoDB, MongoDB Extended JSON
  9. centos5.5 安装git
  10. OCA读书笔记(14) - 备份和恢复基本概念
  11. new TimerTask(robot)(转)
  12. 字符串查找KMP算法(转)
  13. 如何部署Java_web项目到云服务器上
  14. 在 Tomcat 上配置虚拟主机
  15. github上fork了别人的项目后,再同步更新别人的提交
  16. HTML 部分非常用标签
  17. python_flask 基础巩固 (URL_FOR 详解)
  18. 基于pygame实现飞机大战【面向过程】
  19. java依赖的斗争:依赖倒置、控制反转和依赖注入
  20. Sequentially-consistent ordering

热门文章

  1. jpa基本常识
  2. Spring循环依赖的问题
  3. 082 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 02 构造方法介绍 01 构造方法-无参构造方法
  4. .NET 云原生架构师训练营(模块一 架构师与云原生)--学习笔记
  5. jquery购物车全选,取消全选,计算总金额
  6. 【手摸手,带你搭建前后端分离商城系统】02 VUE-CLI 脚手架生成基本项目,axios配置请求、解决跨域问题
  7. centos8上安装phpmyadmin5
  8. Markdown基础知识
  9. xUtils简介和使用方法
  10. Spring 缓存抽象