MySQL--事务持久化原理探究

news2025/1/7 22:22:50

1 引言

如果让你来实现一下 MySQL 持久化的功能,你准备如何实现?如果不考虑性能,接口完全使用同步机制实现,好像也不会出现什么问题,可是不考虑性能是不现实的,IO操作多么耗时,每次写磁盘?那 MySQL 恐怕完全无法支持企业级应用。

考虑加缓存,写磁盘之前先写内存,然后异步持久化,由此带来最棘手的问题,如何保证数据一致性?

我们要保证的是,即使数据持久化变为异步操作,在服务器发生宕机、断电等意外情况时,依旧能够将数据正确的持久化——已写入内存的数据不会因为意外情况导致未持久化(数据丢失)、异步持久化数据的过程保证原子性,要么全部持久化成功,要么全部失败,不能写入不完整的数据(极端情况下一个数据由 128 字节组成,但只持久化了 64 字节)。

来看 MySQL 如何提升性能、并解决数据一致性的问题。

2 直接写磁盘效率低下解决方案

2.1 InnoDB buffer pool的引入

不同的存储引擎有不同的实现,这里来看 InnoDB 是如何增加缓存层的。

MySQL InnoDB Buffer Pool,MySQL InnoDB 缓冲池。里面缓存着大量数据(数据页),使 CPU 读取或写入数据时,不直接和低速的磁盘打交道,直接和缓冲区进行交互,从而解决了因为磁盘性能慢导致的数据库性能差的问题。

数据页:InnoDB 中,数据管理的最小单位为页,默认是 16KB。详见:计算机存储术语:扇区,磁盘块,页。

InnoDB buffer pool详解:MySQL–如何加速读写速度?详解Buffer Pool。

2.2 数据一致性的保障

在了解了 buffer pool 之后,要保证数据一致性,则要进行刷脏。如果每次写操作都要进行同步刷脏,则性能依旧会很差,因此一般都会在一定时机进行批量刷脏(即异步批量刷脏)。这时,要保证的是如何在宕机、断电的情况下,脏页依旧能够正确刷入磁盘,即InnoDB 的刷脏策略是如何实现的

2.2.1 Write-Ahead Log机制

InnoDB 刷脏时采用的是WAL(Write Ahead Log)机制,即先写日志,再写入磁盘。InnoDB 通过 redo log 日志实现了 WAL,让 MySQL 拥有了崩溃恢复能力。一般来说,任何要实现数据持久化的系统,都需要采用 Write-Ahead Log 机制。

WAL机制解决了如下问题:

  1. 数据持久化不受影响(日志本身就保存在磁盘上,写日志成功就意味着数据已经持久化)
  2. 提高了写性能
  3. 避免了在发生宕机、断点等情况下,给磁盘中写入不完整数据情况的发生

关于顺序IO和随机IO我在这里提一嘴,感觉网上说的都不是很清晰:

磁盘的默认行为是随机IO,顺序 IO 需要应用程序或者文件系统进行控制,产生随机 IO 的原因是磁盘上的数据随时在变化(增、删),这样会导致磁盘产生空间碎片(本来1,2,3是在同一磁道顺序存储,但删除了 2 后,这部分空间就会被释放,然后可能写入其他数据),随着时间的推移,原本的顺序 IO 就会变成随机 IO。

我们说的写日志,如果不提前指定日志的大小,告诉磁盘一次性分配多大的连续空间,即使我们是追加写日志,磁盘实际还是在做随机 IO。

数据库中的数据本身是无组织的,伴随着增删,最终会演变为随机 IO,而 B+ 树索引是有结构和一定顺序的数据,因此可以“粗暴”的理解为往磁盘上读写索引是顺序 IO。

其实顺序 IO 比随机 IO 快已经是好几年前的说法了,目前由于硬件的提升,像 SSD 等硬盘的出现,随机 IO 已经不比顺序 IO慢多少了。

最后,Java 中有专门控制顺序 IO 的 API,感兴趣的可以了解下。

1.对于第二点的说明——redo log如何提升写性能?

随机写转顺序写:如果没有 redo log,则脏页的刷新,从内存刷至磁盘完全是随机IO,而redo log中记录的是事务的更新内容(好比将主键id=1的数据行,其age字段改为0,是一种行为日志),而非真正的数据,因此,通过 redo log 可将随机的脏页写入变成顺序的日志刷盘,真正的脏页刷新流程由后台线程去做。

更详细的解释可参考:顺序io和随机io分别在什么情况下出现?为什么日志是顺序io,数据是随机io?- 白竹蓝的回答。

2.对于第三点的说明——redo log如何保证数据一致性?

如果不考虑 bin log,我们可以大致理解为 MySQL 写数据的流程是这样的:buffer pool 中更新好数据后,同步写 redo log,redo log 写成功后,当前事务才能提交成功,否则事务失败。剩下的工作,redo log 刷脏,交给异步线程去做即可。由于 redo log 的写入是同步操作,因此 buffer pool 同步至 redo log 保证了数据一致性。由于 redo log 本身已经落盘,因此不管发生宕机还是断电,MySQL 都有能力从上次消费 redo log 的地方开始接着消费,从而保证了数据的最终一致性(不会发生数据丢失)。

但事实是,企业级应用中,MySQL 一般都是主从架构,由于 bin log 的存在,因此 redo
log 采用了两阶段提交,既保证了主从架构的数据一致性,也保证了本机的 crash-safe,关于 redo log 的两阶段提交,详见MySQL 为什么需要两阶段提交?。

3 详解 redo log

接下来系统的学习一下 redo log。

3.1 redo log简介

InnoDB 引擎独有的日志模块,redo log 通常是物理日志,记录的是数据页的物理修改。

3.2 redo log作用

  1. 提高 MySQL 写操作效率——直接写磁盘是随机写,写 redo log 是顺序写
  2. 防止在发生故障的时间点,尚有脏页未写入磁盘,在重启 MySQL 服务的时候,根据 redo log 进行重做,从而达到事务的持久性这一特性

3.3 需要什么样的redo log

redo log 的维护增加了一份写盘数据,写盘时间会直接影响系统吞吐。显而易见,redo log 的数据量要尽量少。其次,系统崩溃总是发生在始料未及的时候,当 MySQL 重启时,系统并不知道 redo log 中哪些 Page 已经落盘,因此 redo 操作要保证幂等。最后,为了便于通过并发的方式加快重启恢复速度,redo 应该是基于 Page 的,即一个 redo 只涉及一个 Page 的修改。

物理日志 vs 逻辑日志

物理日志(physical log)占用一片连续的磁盘空间。其主要目的是为系统进行快速恢复提供原始数据映像。MySQL 中的物理日志更偏向于记录了在某个数据页上做了什么修改。

逻辑日志(logical logs)是由若干块独立的磁盘空间构成,而每一块都是连续的磁盘空间。MySQL 中的逻辑日志更偏向于记录的是一条 SQL 语句的原始逻辑,例如给某一个字段 +1。

3.4 redo log buffer

在了解 redo log 之前,首先要认识一下 redo log buffer。

redo log 包括两部分:一是内存中的重做日志缓冲(redo log buffer),该部分日志是易失的;二是磁盘上的重做日志文件(redo log file),该部分日志是持久的。

注:下文中所述的“刷到磁盘上”都指刷到 redo log file 中

那么为什么会加入 redo log buffer 呢?

在概念上,innodb 通过 force log at commit 机制实现事务的持久性,即在事务提交的时候,必须先将该事务的所有事务日志写入到磁盘上的 redo log file 中进行持久化。

以一个 update 操作为例,来了解 redo log 的更新流程:

image.png

综上,在事务提交前就要将 redo log 的内容写入磁盘,并且由于 redo log 记录的是数据库物理页的变化,因此 redo log 文件中一个事务可能有多条记录,最后一个提交的事务记录会覆盖所有未提交的事务记录。

例如事务 T1,可能在 redo log 中记录了 T1-1, T1-2, T1-3, T1* 共 4 个操作,其中 T1* 表示最后提交时的日志记录,所以对应数据页的最终状态是 T1* 对应的操作结果。

如果没有 redo log buffer,每次产生的事务记录都要刷到磁盘上,便会造成磁盘 IO 升高,性能降低,并且也不利于大事务的运行

为了确保每次事务提交后 redo log buffer 中的内容都能写入到 redo log file 中,InnoDB 存储引擎会调用一次操作系统的 fsync 操作(即 fsync() 系统调用)。因为 MySQL 是工作在用户空间的,因此 redo log buffer 处于用户空间的内存中,要写入到磁盘上的 redo log file 中,中间还要经过操作系统内核空间的 os buffer,调用 fsync() 的作用就是将 OS buffer 中的日志刷到磁盘上的 redo log file 中。

从 redo log buffer 写日志到磁盘的 redo log file 中,过程如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NY26tQyV-1685706808344)(//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/04d854f862cf4edba91f332a1901c8b0~tplv-k3u1fbpfcp-zoom-1.image)]

之所以要经过一层 OS buffer,是因为 open 日志文件的时候,open 没有使用 O_DIRECT 标志位。该标志位意味着绕过操作系统层的 OS buffer,IO 直接写到底层存储设备。不使用该标志位意味着将日志进行缓冲,缓冲到了一定容量,或者显式调用 fsync() 才会将缓冲中的内容刷到存储设备。比如写 abcde,不使用 O_DIRECT 将只发起一次系统调用,使用 O_DIRECT 将发起 5 次系统调用。

MySQL 支持用户自定义在 commit 时如何将 redo log buffer 中的日志刷 redo log file 中。这种控制通过变量 innodb_flush_log_at_trx_commit 的值来决定。该变量有 3 种值:0、1、2,默认为 1。但注意,这个变量只是控制 commit 动作是否刷新 redo log buffer 到磁盘。

  • 当设置为 1 的时候,事务每次提交时都会将 redo log buffer 中的日志写入 os buffer 并调用 fsync() 刷到 redo log file 中(同步)。这种方式即使系统崩溃也不会丢失任何数据,但是因为每次提交都要写入磁盘,IO 的性能较差

  • 当设置为 0 的时候,事务提交时不会将 redo log buffer 中的日志写入到 os buffer,而是每秒写入 os buffer 并调用 fsync() 写入到 redo log file 中,也就是说设置为 0 时是(大约)每秒刷新写入到磁盘中的。这种情况下当 MySQL 进程崩溃时,便会丢失 1 秒钟的数据

  • 当设置为 2 的时候,每次提交都写入到 os buffer,然后每秒调用 fsync() 将 os buffer 中的日志写入到 redo log file。这种情况下只有当操作系统崩溃或者系统掉电,才会丢失 1 秒钟的数据

上面说到的“最后 1s”并不是绝对的,有时会丢失更多数据。有时由于调度问题,每秒刷写(once-per-second flushing) 并不能保证 100% 执行。对于一些数据一致性和完整性要求不高的应用,配置为 2 就足够了,如果为了最高性能,可以设置为 0;有些应用,如支付服务,对一致性和完整性要求很高,所以即使最慢,也要设置为 1。

4 参考阅读

  1. 详细分析MySQL事务日志(redo log和undo log)
  2. 『MySQL』深入理解事务的来龙去脉
  3. MySQL参数:innodb_flush_log_at_trx_commit 和 sync_binlog
  4. 庖丁解InnoDB之REDO LOG
  5. MySQL事务和持久化原理

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

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

相关文章

2023第五届双态IT北京用户大会 | 一起见证云原生时代的数据魅力

2023年6月9日-11日,由ITSS分会指导,ITSS数据中心运营管理组、双态IT论坛联合主办,ITSS媒体组协办的“2023第五届双态IT北京用户大会”将于北京召开。 为了能够有更多专注细分领域、内容深入的分享和探讨,每一届都会和论坛成员一起…

ReID专栏(二)多尺度设计与应用

前言 多尺度流层可以更有效地提取多尺度信息,而基于显著性的学习融合层有利于重要特征通道的自动选择,因此MuDeep在学习鉴别模式方面很强大。其实这也是目前大多数深度学习任务发表论文的趋势,即多尺度、显著性特征的表示。 本教程禁止转载。…

OpenMMLab AI实战营第二期(2)MMPose初体验

根据MMPose的官方文档学习一下 MMPose文档地址:https://mmpose.readthedocs.io/zh_CN/latest/index.html 文章目录 1. 概述2. 安装2.1 创建conda环境并激活2.2 安装pytorch2.3 使用 MIM 安装 MMEngine 和 MMCV2.4 安装MMPose 3. 20 分钟了解 MMPose 架构设计3.1 总…

Typora导出pdf一直停留在导出界面

Typora导出pdf一直停留在导出界面 1. 因为修改临时文件夹地址导致的问题 尝试遍了网上的各种方法对我都没奏效。 但是用管理员权限在 C:\Program Files\Typora 下打开 typora.exe 发现可以正常导出 pdf。 结合这一点,加上最近频繁出现启动文件、文件夹没有权限的…

ES查询 too_many_clauses,maxClauseCount is set to 5000

一:语法执行背景 ES boo查询中过多的拼接bool导致报maxClauseCount is set to 5000 { "caused_by": { "type": "too_many_clauses","reason": "maxClauseCount is set to 5000" } } 查询DSL语句: { …

问题解决:微信开发者工具显示清除登录状态失败 TypeError: Failed to fetch [1.06.2303220][win32-x64]

问题: 在编译之前,会进行清除全部缓存操作,但是点击后出现清除清除登录状态失败。 清除登录状态失败 TypeError: Failed to fetch [1.06.2303220][win32-x64]原因: 未连接网络,或者被你连接的网络拦截。 解决&#xf…

基于 PaddleVideo 的花滑骨骼点动作识别 2s-AGCN

配置文件节点流配置文件 2s-agcn_ntucs_joint_fsd.yamlMODEL 字段DATASET 字段PIPELINE 和 INFERENCE 字段OPTIMIZER 字段 agcn2s.pygraph输入通道数 骨骼流 Dataset 和 Pipeline配置文件DATASETPIPELINE 源码skeleton.pyskeleton_pipeline.pyAutoPaddingSkeletonNormIdenSkete…

springboot+vue高校学科建设资源管理系统

本 1、数据库的构建:数据库需求分析根据所需要实现的功能,对其数据库的需求进行分析。 2、后台管理:网站后台是给管理员用作管理网站内容的操作界面,它是网站日常维护,网站内容操作的交互窗口,是完成网站后…

JS逆向系列之猿人学爬虫第9题-动态cookie2

文章目录 目标参数流程分析js代码Python调用测试目标 https://match.yuanrenxue.cn/match/9参数流程分析 二次请求cookie携带m 第一次请求响应内容格式化之后是这样的: < body > < script src = "/static/match/safety/match9/udc.js" > <

Axure教程—表格(中继器)

本文将教大家如何用AXURE中的中继器制作表格 一、效果介绍 如图&#xff1a; 预览地址&#xff1a;https://oc3e6a.axshare.com 下载地址&#xff1a;https://download.csdn.net/download/weixin_43516258/87854863?spm1001.2014.3001.5501 二、功能介绍 可以在表格中插入…

1729_c语言中全局变量与局部变量以及形参重名问题

全部学习汇总&#xff1a; GreyZhang/c_basic: little bits of c. (github.com) 欢迎路过的YUAN类朋友相互交流&#xff0c;以下是我的联系方式&#xff1a; Email&#xff1a;greyzhang126.com 微信&#xff1a;grey0612 C语言中烦人变量命名冲突时会有怎么样的处理机制&…

sql_server数据库入门学习(一)

&#x1f3ac; 博客主页&#xff1a;博主链接 &#x1f3a5; 本文由 M malloc 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f384; 学习专栏推荐&#xff1a;LeetCode刷题集&#xff01; &#x1f3c5; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指…

react-native-vector-icons 安卓和ios 安装

npm install --save react-native-vector-icons ios 将node_modules/react-native-vector-icons/Fonts的文件夹里的内容复制到项目的ios/Fonts文件夹下 选择Ios/Fonts/里的内容 点开Ifon.plist文件&#xff0c;并在右侧添加一项"Fonts provided by application" 在…

(学习日记)2023.04.25

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

[SpringBoot]Knife4j框架Knife4j的显示内容的配置

目录 Knife4j框架 使用 添加依赖&#xff1a;knife4j-spring-boot-starter&#xff0c;版本2.0.9 添加配置&#xff1a;在配置文件中添加knife4j.enable属性的配置&#xff0c;取值为true 添加配置类&#xff1a;类的代码相对固定 访问 Knife4j的显示内容的配置 Knife4j…

赶紧收藏!2023年成人高考【复习大纲】

▶高起点考试题型 高起点-语文 试卷满分为150分。考试用时120分钟。 >>>考试内容&#xff1a; 语言知识及运用、现代文阅读、古代诗文阅读和鉴赏、写作四个方面&#xff0c;考试以测试阅读和写作能力为重点。 >>>备考分析&#xff1a; 语文学习要注意平…

【Python】实现一个类似于Thief的摸鱼软件

一 背景说明 之前用Thief摸鱼(Thief官网&#xff09;&#xff0c;觉得挺好用。对于其最基本的TXT摸鱼&#xff0c;准备在Python中扩展一下功能&#xff0c;使其能够通过爬虫&#xff0c;支持爬取热门小说网站的内容。 软件已经开源到&#xff1a;MoFish软件开源地址 功能是&…

ReID专栏(一) 任务与数据集概述

前言 本专栏针对Closed-world的ReID任务&#xff0c;首先介绍本任务的目标与主要数据集&#xff0c;包括行人重识别、跨模态行人重识别与车辆重识别。然后从三类表征学习的角度解读相关论文&#xff0c;表征学习是本任务的核心&#xff0c;大量重识别工作都致力于提高表征学习的…

uboot源码结构、配置、编译

目录 1.uboot源码结构 1.1uboot源码获取 1.2 uboot的特点 1.3 uboot源码结构 2、uboot配置与编译 2.1uboot配置 2.2 uboot编译 1.uboot源码结构 1.1uboot源码获取 uboot是一个开源的软件。开源有两层含义。一是可以免费用、二是开放源代码 uboot源码下载 http://www.denx…

记一次线上MQ堆积问题处理

背景 线上订单服务10个POD节点&#xff0c;某kafka tpoic原有6个分区。 问题现象 上午11.30左右一波下单高峰&#xff0c;根据分布式消息告警发现有少量消息堆积&#xff0c;但是堆积量不大&#xff0c;持续几分钟后恢复正常。 下午1点后出现新一轮高峰&#xff0c;出现了大…