【MySQL】双写、重做日志对宕机时脏页数据落盘的作用的疑问及浅析

news2024/11/18 5:41:16

众所周知,双写机制、重做日志文件是mysql的InnoDB引擎的几个重要特性之二。其中两者的作用都是什么,很多文章都有分析,如,双写机制(Double Write)是mysql在crash后恢复的机制,而重做日志文件(Redo Log File)也是mysql崩溃恢复机制的一部分。但是,这两者是如何保证mysql能够正确恢复数据的呢?对此,我在看书查证和思考过程中,产生了很多的疑问,也有一些收获,写在下面。

一,介绍双写机制、重做日志文件

首先,我先介绍或者说概括一下双写机制,和重做日志文件。在《MySQL技术内幕 InnoDB技术引擎》[1]一书中,双写机制部分介绍原文如下:

如果说Insert Buffer带给InnoDB存储引擎的是性能上的提升,那么doublewrite(两次写)带给InnoDB存储引擎的是数据页的可靠性。
当发生数据库宕机时,可能InnoDB存储引擎正在写入某个页到表中,而这个页只写了一部分,比如16kb的页,只写了前4kb,之后就发生了宕机,这种情况被成为部分写失效(partial page write)。在InnoDB存储引擎未使用doublewrte技术前,曾经出现过因为部分写失效而导致数据丢失的情况。
有经验的DBA也许会想,如果发生写失效,可以通过重做日志进行恢复。这是一个办法,但是必须清楚的意识到,重做日志中记录的是对页的物理操作,如偏移量800,写’aaa’记录。如果这个页本身已经发生了损坏,再对其进行重做是没有意义的。这就是说,在应用(apply)重做日志之前,用户需要一个页的副本,当写入失效发生时,先通过页的副本来还原该页,再进行重做,这就是doublewrite。在InnoDB存储引擎中doublewrite的体系架构如图2-5所示。
doublewrite由2部分组成,一部分是内存中的doublewrite buffer,大小为2MB,另一部分是物理磁盘上共享表空间中连续的128个页,即2个区(extent),大小同样为2MB。在对缓冲池的脏页进行刷新时,并不直接写磁盘,而是会通过memcpy函数将脏页先复制到内存中的doublewrite buffer,之后通过doublewrite buffer再分两次,每次1MB顺序的写入共享表空间的物理磁盘上,然后马上调用fsync函数,同步磁盘,避免缓冲写带来的问题。在这个过程中,因为doublewrite页是连续的,因此这个过程是顺序写的,开销并不是很大。在完成doublewrite页的写入后,再将doublewrite buffer中的页写入各个表空间文件中,此时的写入则是离散的。

重做日志部分原文如下:

在默认情况下,在InnoDB存储引擎的数据目录下会有2个名为ib_logfile0和ib_logfile1的文件。在mysql官方手册中将其称为InnoDB存储引擎的日志文件,不过准确的定义应该是重做日志文件(redo log file)。为什么强调是重做日志文件呢?因为重做日志文件对于InnoDB存储引擎至关重要,它们记录了对于innoDB存储引擎的事务日志。
当实例或介质失败(media failure)时,重做日志文件就能派上用场。例如,数据库由于所在主机掉电导致实例失败,InnoDB存储引擎会使用重做日志恢复到掉电前的时刻,以此来保证数据的完整性。
每个InnoDB存储引擎至少有1个重做日志文件组(group),每个文件组下至少有2个重做日志文件,如默认的ib_logfile0和ib_logfile1。为了得到更高的可靠性,用户可以设置多个的镜像日志组(mirrored log groups),将不同的文件组放在不同的磁盘上,以此提高重做日志的高可用性。在日志组中每个重做日志文件的大小一致,并以循环写入的方式运行。InnoDB存储引擎先写重做日志文件1,当达到文件的最后时,会切换至重做日志文件2,再当重做日志文件2也被写满时,会再切换到重做日志文件1中。

双写和重做日志不是单独运行的,这两者是结合起来运行的。在文章[4]中,描述了双写机制、重做日志运行的原理

在这里插入图片描述

第一步,master thread会每秒将写操作的重做日志缓冲刷写到redo log中。
第二步,通过memcpy函数将脏页copy到doubewrite buffer(内存)中。
第三步,将doublewrite buffer第一次刷写fsync到磁盘(系统表空间)。
第四步,将doublewrite buffer第二次刷写fsync到磁盘(独立表空间)。

二,问题及思考结果

1,双写机制的作用是什么?
书中[1]写到,双写机制的作用是为了应对部分页写入的情况。实际上,不止是书中写的”16kb的页,只写了前4kb,就发生了宕机“这种情况。在写入的时候,怎么会完完整整的写完一个Linux的页才宕机呢?(32位linux系统支持4k的页[2])

实际上,更可能的是,假设一个16kb的脏页,写入了前4k的页,正在写入第2个4k的linux页,结果断电了。此时导致的必然结果就是第二个页的格式错误。

这种部分页写入的情况,会导致数据的格式错误,无法通过mysql的自检,进而需要双写机制来恢复数据。

因此,你看到书中[1]举的栗子中,mysql在使用双写机制恢复的时候,Info信息是:

InnoDB: Crash recovery may have failed for some .ibd files!
InnoDB: Restoring possible half-written data pages from the doublewrite

也就是说,双写机制的作用,是恢复因为部分页写入导致发生数据格式错误的页。

2,双写机制也需要写入系统表空间的磁盘中,同样的数据,也要第二次写入数据所在的独立表空间的磁盘空间,那有没有可能,在双写机制向系统表空间中写入(第一次写)的时候crash断电呢?

书中[1]写到:
doublewrite由2部分组成,一部分是内存中的doublewrite buffer,大小为2MB,另一部分是物理磁盘上共享表空间中连续的128个页,即2个区(extent),大小同样是2MB。在对缓冲池的脏页进行刷新时,并不直接写磁盘,而是会通过memcpy函数将脏页先复制到内存中的doublewrite buffer,然后马上调用fsync函数,同步磁盘,避免缓冲写带来的问题。在这个过程中,因为doublewrite页是连续的,因此这个过程是顺序写的,开销并不是很大。在完成doublewrite页的写入后,再将doublewrite buffer中的页写入各个表空间文件中,此时的写入是离散的。

首先,我们需要知道,随机IO和顺序IO在性能上的区别。在《高性能mysql》[3]中,进行了随机IO和顺序IO的对比,在举例中描述”相比之下顺序读可以每秒读500000行——是随机读的5000“倍。这是3个数量级的差别,即使栗子中的情况和实际机器的性能、实际读写的情况有差别,也不会差太多。也就是说,随机IO和顺序IO是有大约三四个数量级上的速度差别的。

双写机制的2次写是连续的,总共为2MB。mysql每秒最多刷新100个脏页到独立表空间中。100个脏页为1600kb,即1.6MB,因此可以说双写机制提供的2MB是够用的。但是问题是这100个脏页可能是随机IO。

假设master thread的写入压力比较大,每秒刷新100个脏页,会第一步先向redo log磁盘文件中写入、第二步将脏页memcpy到内存中的双写buffer中,第三步是将双写buffer fsync到系统表空间的双写文件中,最后,将这100个脏页刷写到独立表空间的磁盘文件中。2MB的数据,第1、3步都是顺序IO,磁盘寻址一次定位到文件后,会顺序写入2MB数据即可,第2步是内存中的复制,只有第4步,是要将100个mysql页随机IO写入磁盘,而一个32位linux系统支持4kb的页,意味着系统需要在磁盘中寻址100*(16 / 4)= 400次。(怕读者们混乱,linux系统的页举例使用了《MySQL技术内幕》中举例的4kb大小的页,而不是64位系统的8kb页[2])

总之,不难发现,仅以双写机制第一次写入:将数据写入系统表空间,和第二次写入:将数据写入磁盘的独立表空间而言,第一次写入仅需寻址1次,顺序写入2MB数据;而第二次写入,需要最多400次寻址。意味着第一次写入占用的时间仅占总寻址时间的1/(400+1)。因此双写机制在第一次写入时crash的几率是非常小的。(数据的写入时间这里不作讨论,随机IO主要时间在磁盘寻址上)。

至于mysql有没有可能在第一次写入的时候崩溃呢,当然有啦。我很喜欢周sang的那句话,中间件的设计中,很多都是balance,做权衡InnoDB设计的双写机制,以写入磁盘的1/400寻址时间的性能开销,以及这段时间内crash数据无法正确恢复的微小可能,换来可以保证crash崩溃断电这种极端情况发生时,数据错误时能够恢复到正确的格式,以及配合redo log,InnoDB可以异步写入而不担心数据丢失,无疑是值得的。

3,类似问题2,redo log会不会也在写入的时候crash呢?

有可能,但是可能性很小。

我们看《MySQL技术内幕》[1]中讲述到,
(InnoDB)每秒一次的操作包括:
日志缓冲刷新到磁盘,即使这个事务还没有提交(总是)
合并插入缓冲(可能)
至多刷新100个InnoDB的缓冲池中的脏页到磁盘(可能)
如果当前没有用户活动,则切换到background loop(可能)

日志缓冲指的就是重做日志缓冲(redo log buffer)。此外,引擎会在事务提交时也将日志缓冲刷新到磁盘。

综上可以看出,其实重做日志缓冲会非常勤奋的刷新到redo log中,这样可以保证不会将一次事务的所有刷新磁盘的压力都堆积到一次刷新中。

其次,redo log是顺序IO,会在默认的两个redo log中循环写,一个文件写满了就在另一个文件中写(脏数据写入磁盘后会从redo log中删除)。从问题2中我们讲了,顺序IO就是要比同数据量的随机IO快。

最后[1],InnoDB存储引擎的重做日志文件记录的是关于每个页(page)的更改的物理情况,如偏移量800,写’aaa’记录。这样就保证了每次写操作需要记录的redo log的大小很小。

综上所述,使得redo log的写入很快,远快于redo log记录的写操作在磁盘中写入的时间。

4,假设写入压力很大,mysql每秒100个脏页写入都无法把缓冲池中的脏页刷写完,那么在crash后,仅靠双写的2MB数据无法完全恢复数据,那么要怎么恢复数据呢?
首先需要明确,那个2MB大小的双写文件,不是为了恢复数据用的,它仅仅是为了保证写入时发生crash导致部分页写入,用来恢复发生部分页写入那部分页的正确格式的。至于恢复缓冲池中的脏页数据,是redo log的活儿。

《MySQL技术内幕》[1]中,写到,在InnoDB版本1.2.x之前,redo log总大小不得超过4GB,在1.2.x之后,限制扩大到512GB。

也就是说,即使mysql的写入压力很大,每秒100个脏页的写入都不够,InnoDB的缓冲池中积压了很多旧的写入请求的脏页,还有很多脏页没有写入磁盘,但是它们都已经写入redo log,最多512G,它们这些脏页都可以写入到redo log中。

而新的写入操作,也会每秒的日志缓冲刷写到磁盘的redo log中,最大限度的保存脏页数据。

三,写在最后

其实从这里我个人觉得,双写机制和重做日志文件,是InnoDB引擎能够实现异步IO的关键,它们结合起来最大限度的保证了即使是异步IO,内存中的脏页数据最后也可以落盘,即事务的持久性,并且崩溃不会导致部分页写入。而异步IO,无疑很大的提升了InnoDB的写入性能。

或者更抽象的说,InnoDB在做了一定程度的design and balance后,以微小的性能开销和很小的数据丢失风险,保证了InnoDB的异步IO和、页数据的持久性以及crash时的数据正确性。

在谈双写机制时,离不开重做日志保证数据完整性,在谈重做日志文件时,离不开双写机制保证数据最后写的页不发生部分页写入。

参考文章
[1],MySQL技术内幕 InnoDB技术引擎, 作者:姜承尧
[2],Linux内核设计与实现,作者:Robert Love
[3],高性能mysql,作者:Baron Scbwartz,Peter Zaitsev,Vadim Tkacbenko
[4],MySQL双写缓冲区(Doublewrite Buffer)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1419473.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网安文件包含漏洞

文件包含概念: 开发人员通常会把可重复使用的函数写到单个文件中,在使用某些函数时,直接调用此文件,而无需再次编写,这种调用文件的过程一般被称为包含。为了使代码更加灵活,通常会将被包含的文件设置为变…

docker+jekins+maven+ssh 持续集成交付部署 jar包

一. docker环境搭建,此处略过。 二. docker部署jekins 2.1 拉取镜像,挂载工作目录,xxxx为宿主机指定工作目录 docker pull jenkins/jenkins docker run -d -p 8080:8080 -p 50000:50000 --name jenkins --privilegedtrue -v xxxxxxxxxx:/var/jenkins…

源码安装nginx并提供服务脚本

一、下载nginx ①官网复制下载链接 ②在Linux中下载 [rootopenEuler2 ~]# wget -c https://nginx.org/download/nginx-1.24.0.tar.gz 二、解压并指定路径 [rootopenEuler2 ~]# tar xf nginx-1.24.0.tar.gz -C /usr/local/src/ 三、安装依赖 dnf install -y gcc gcc-c mak…

使用plotly dash 画3d圆柱(Python)

plotly3D (3d charts in Python)可以画3维图形 在做圆柱的3D装箱项目,需要装箱的可视化,但是Mesh (3d mesh plots in Python)只能画三角形,所以需要用多个三角形拼成一个圆柱(想做立…

vue实现跳转传参查询

vue实现跳转传参查询: 应用场景:外部链接携参跳转目标页时,避免多次输入查询信息查询 目标需求:登录及非登录状态均可跳转自动查询 避坑指南:token失效时需要重新缓存及路由导航缓存判断 简单实现:缓存信息&#xff0c…

PeakCAN连接到WSL2 Debian

操作步骤 按照以下步骤进行操作: 在Windows下安装PeakCAN驱动并安装,地址是https://www.peak-system.com/PCAN-USB.199.0.html?&L1 在Windows下安装usbipd,地址是https://github.com/dorssel/usbipd-win/releases,最新版是…

Elasticsearch:如何为 Elastic Stack 配置 AI Assistant

了解并安装 Elastic AI Assistant Elastic 推出了 Observability AI Assistant,这是一款利用生成式 AI 来增强你的 Observability 体验的强大工具。 该 AI 助手由 OpenAI 或 Azure OpenAI 服务的连接器提供支持,可带来上下文洞察和聊天功能,…

vue中的vuex

在Windows的应用程序开发中,我们习惯了变量(对象)声明和使用方式,就是有全局和局部之分,定义好了全局变量(对象)以后在其他窗体中就可以使用,但是窗体之间的变量(对象&am…

c++学习记录 多态—案例2—电脑组装

#include<iostream> using namespace std;//抽象不同的零件//抽象的cpu类 class Cpu { public://抽象的计算函数virtual void calculate() 0; };//抽象的显卡类 class VideoCard { public://抽象的显示函数virtual void display() 0; };//抽象的内存条类 class Memory …

【MySQL】聚合函数和内置函数

文章目录 1 :peach:聚合函数:peach:2 :peach:group by子句的使用:peach:3 :peach:内置函数:peach:3.1 :apple:日期函数:apple:3.2 :apple:字符串函数:apple:3.3 :apple:数学函数:apple: 4 :peach:其它函数:peach: 1 &#x1f351;聚合函数&#x1f351; 函数说明COUNT([DISTIN…

【大数据】Flink 架构(三):事件时间处理

《Flink 架构》系列&#xff08;已完结&#xff09;&#xff0c;共包含以下 6 篇文章&#xff1a; Flink 架构&#xff08;一&#xff09;&#xff1a;系统架构Flink 架构&#xff08;二&#xff09;&#xff1a;数据传输Flink 架构&#xff08;三&#xff09;&#xff1a;事件…

(十四)测频NE555应用

文章目录 NE555简介NE555原理图如何对NET_SIG&#xff08;P34&#xff09;引脚计脉冲测频应用部分代码现象 NE555简介 NE555是一种集成电路&#xff0c;其内部结构包括比较器、RS触发器、电压比较器和输出级三个主要功能模块。是由硬件直接产生的脉冲 NE555一些的常见应用&am…

Orion-14B-Chat-Plugin本地部署的解决方案

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

PGsql 解析json及json数组

创建测试数据 drop table if exists json_test; create table json_test as select 111 as id, {"nodes":{"1692328028076":{"nodeId":"1692328028076","nodeName":"测试表1","nodeType":"DATACO…

Golang 流媒体服务器lalserver使用指南

目录 安装 使用 1.推流 2.播放 官方地址 安装 1.下载源码 wget https://github.com/q191201771/lal/releases/download/v0.36.7/lal_v0.36.7_linux.zipunzip lal_v0.36.7_linux.zip cd lal_v0.36.7_linux 2.启动 ./bin/lalserver -c ./conf/lalserver.conf.json 使用 …

Linux部署lomp环境,安装typecho、WordPress博客

部署lomp环境&#xff0c;安装typecho、WordPress博客 一、环境要求1.1.版本信息1.2.准备阿里云服务器【新用户免费使用三个月】1.3.准备远程工具【FinalShell】 二、Linux下安装openresty三、Linux下安装Mysql四、安装Apache【此步骤可省略】4.1.安装Apache服务及其扩展包4.2.…

【React教程】(2) React之JSX入门与列表渲染、条件渲染详细代码示例

目录 JSX环境配置基本语法规则在 JSX 中嵌入 JavaScript 表达式在 JavaScript 表达式中嵌入 JSXJSX 中的节点属性声明子节点JSX 自动阻止注入攻击在 JSX 中使用注释JSX 原理列表循环DOM Elements 列表渲染语法高亮 条件渲染示例1&#xff1a;示例2&#xff1a;示例3&#xff08…

使用Docker安装Jenkins,并能够在该Jenkins中使用Docker

1. 构建Dockerfile 试错1 参考https://medium.com/manav503/how-to-build-docker-images-inside-a-jenkins-container-d59944102f30 按照文章里所介绍的&#xff0c;实现在Jenkins容器环境中依然能够调用Docker&#xff0c;需要做到以下几步 下载Jenkins镜像将环境中的docke…

解析PDF二维码:数字时代文件管理的创新之道

随着数字时代的来临&#xff0c;文件管理方式正经历着翻天覆地的变革。在这个变革的浪潮中&#xff0c;PDF二维码作为一种创新的技术手段&#xff0c;正逐渐引起人们的关注。本文将深入探讨PDF二维码的概念、应用领域以及在文件管理中的前景。 一、PDF二维码的概念 PDF二维码…

2024-01-29 ubuntu 用脚本设置安装交叉编译工具链路径方法,设置PATH环境变量

一、设置PATH环境变量的方法,建议用~/.bash_profile的方法&#xff0c;不然在ssh登录的时候可能没有设置PATH. 二、下面的完整的脚本&#xff0c;里面的echo "export PATH$build_toolchain_path:\$PATH" >> $HOME/.bashrc 就是把交叉编译路径写写到.bashrc设置…