【MySQL】MySQL进阶之路(六)MySQL三大日志(binlog、redo log和undo log)详解

2023-05-16

写在前面的话

脑子是个好东西,可惜的是一直没有搞懂脑子的内存删除机制是什么,所以啊,入行多年,零零散散的文章看了无数,却总是学习了很多也忘了很多。

痛定思痛的我决定从今天开始系统的梳理下知识架构,记录下零散的知识,方便温故知新的同时也顺便清除一些大脑空间用来学习更高深的技术。


目录

前言

redo log

刷盘时机

日志文件组

redo log 小结

binlog

记录格式

写入机制

两阶段提交

undo log

总结

站在巨人的肩膀上

MySQL 好文推荐


前言

MySQL 日志 主要包括错误日志、查询日志、慢查询日志、事务日志、二进制日志几大类。其中,比较重要的还要属二进制日志 binlog(归档日志)和事务日志 redo log(重做日志)和 undo log(回滚日志)。

 

今天就来聊聊 redo log(重做日志)、binlog(归档日志)、两阶段提交、undo log (回滚日志)。

redo log

redo log(重做日志)是InnoDB存储引擎独有的,它让MySQL拥有了崩溃恢复能力。

比如 MySQL 实例挂了或宕机了,重启时,InnoDB存储引擎会使用redo log恢复数据,保证数据的持久性与完整性。

MySQL 中数据是以页为单位,你查询一条记录,会从硬盘把一页的数据加载出来,加载出来的数据叫数据页,会放入到 Buffer Pool 中。

后续的查询都是先从 Buffer Pool 中找,没有命中再去硬盘加载,减少硬盘 IO 开销,提升性能。

更新表数据的时候,也是如此,发现 Buffer Pool 里存在要更新的数据,就直接在 Buffer Pool 里更新。

然后会把“在某个数据页上做了什么修改”记录到重做日志缓存(redo log buffer)里,接着刷盘到 redo log 文件里。

图片笔误提示:第 4 步 “清空 redo log buffe 刷盘到 redo 日志中”这句话中的 buffe 应该是 buffer。

理想情况,事务一提交就会进行刷盘操作,但实际上,刷盘的时机是根据策略来进行的。

小贴士:每条 redo 记录由“表空间号+数据页号+偏移量+修改数据长度+具体修改的数据”组成

刷盘时机

InnoDB 存储引擎为 redo log 的刷盘策略提供了 innodb_flush_log_at_trx_commit 参数,它支持三种策略:

  • 0 :设置为 0 的时候,表示每次事务提交时不进行刷盘操作
  • 1 :设置为 1 的时候,表示每次事务提交时都将进行刷盘操作(默认值)
  • 2 :设置为 2 的时候,表示每次事务提交时都只把 redo log buffer 内容写入 page cache

innodb_flush_log_at_trx_commit 参数默认为 1 ,也就是说当事务提交时会调用 fsync 对 redo log 进行刷盘

另外,InnoDB 存储引擎有一个后台线程,每隔1 秒,就会把 redo log buffer 中的内容写到文件系统缓存(page cache),然后调用 fsync 刷盘。

也就是说,一个没有提交事务的 redo log 记录,也可能会刷盘。

为什么呢?

因为在事务执行过程 redo log 记录是会写入redo log buffer 中,这些 redo log 记录会被后台线程刷盘。

除了后台线程每秒1次的轮询操作,还有一种情况,当 redo log buffer 占用的空间即将达到 innodb_log_buffer_size 一半的时候,后台线程会主动刷盘。

下面是不同刷盘策略的流程图。

innodb_flush_log_at_trx_commit=0

0时,如果MySQL挂了或宕机可能会有1秒数据的丢失。

innodb_flush_log_at_trx_commit=1

1时, 只要事务提交成功,redo log记录就一定在硬盘里,不会有任何数据丢失。

如果事务执行期间MySQL挂了或宕机,这部分日志丢了,但是事务并没有提交,所以日志丢了也不会有损失。

innodb_flush_log_at_trx_commit=2

2时, 只要事务提交成功,redo log buffer中的内容只写入文件系统缓存(page cache)。

如果仅仅只是MySQL挂了不会有任何数据丢失,但是宕机可能会有1秒数据的丢失。

日志文件组

硬盘上存储的 redo log 日志文件不只一个,而是以一个日志文件组的形式出现的,每个的redo日志文件大小都是一样的。

比如可以配置为一组4个文件,每个文件的大小是 1GB,整个 redo log 日志文件组可以记录4G的内容。

它采用的是环形数组形式,从头开始写,写到末尾又回到头循环写,如下图所示。

在个日志文件组中还有两个重要的属性,分别是 write pos、checkpoint

  • write pos 是当前记录的位置,一边写一边后移
  • checkpoint 是当前要擦除的位置,也是往后推移

每次刷盘 redo log 记录到日志文件组中,write pos 位置就会后移更新。

每次 MySQL 加载日志文件组恢复数据时,会清空加载过的 redo log 记录,并把 checkpoint 后移更新。

write pos 和 checkpoint 之间的还空着的部分可以用来写入新的 redo log 记录。

如果 write pos 追上 checkpoint ,表示日志文件组满了,这时候不能再写入新的 redo log 记录,MySQL 得停下来,清空一些记录,把 checkpoint 推进一下。

redo log 小结

相信大家都知道 redo log 的作用和它的刷盘时机、存储形式。

现在我们来思考一个问题: 只要每次把修改后的数据页直接刷盘不就好了,还有 redo log 什么事?

它们不都是刷盘么?差别在哪里?

1 Byte = 8bit
1 KB = 1024 Byte
1 MB = 1024 KB
1 GB = 1024 MB
1 TB = 1024 GB

实际上,数据页大小是16KB,刷盘比较耗时,可能就修改了数据页里的几 Byte 数据,有必要把完整的数据页刷盘吗?

而且数据页刷盘是随机写,因为一个数据页对应的位置可能在硬盘文件的随机位置,所以性能是很差。

如果是写 redo log,一行记录可能就占几十 Byte,只包含表空间号、数据页号、磁盘文件偏移 量、更新值,再加上是顺序写,所以刷盘速度很快。

所以用 redo log 形式记录修改内容,性能会远远超过刷数据页的方式,这也让数据库的并发能力更强。

其实内存的数据页在一定时机也会刷盘,我们把这称为页合并,讲 Buffer Pool的时候会对这块细说

binlog

redo log 它是物理日志,记录内容是“在某个数据页上做了什么修改”,属于 InnoDB 存储引擎。

而 binlog 是逻辑日志,记录内容是语句的原始逻辑,类似于“给 ID=2 这一行的 c 字段加 1”,属于MySQL Server 层。

不管用什么存储引擎,只要发生了表数据更新,都会产生 binlog 日志。

那 binlog 到底是用来干嘛的?

可以说MySQL数据库的数据备份、主备、主主、主从都离不开binlog,需要依靠binlog来同步数据,保证数据一致性。

binlog会记录所有涉及更新数据的逻辑操作,并且是顺序写。

记录格式

binlog 日志有三种格式,可以通过binlog_format参数指定。

  • statement
  • row
  • mixed

指定statement,记录的内容是SQL语句原文,比如执行一条update T set update_time=now() where id=1,记录的内容如下。

同步数据时,会执行记录的SQL语句,但是有个问题,update_time=now()这里会获取当前系统时间,直接执行会导致与原库的数据不一致。

为了解决这种问题,我们需要指定为row,记录的内容不再是简单的SQL语句了,还包含操作的具体数据,记录内容如下。

row格式记录的内容看不到详细信息,要通过mysqlbinlog工具解析出来。

update_time=now()变成了具体的时间update_time=1627112756247,条件后面的@1、@2、@3 都是该行数据第 1 个~3 个字段的原始值(假设这张表只有 3 个字段)。

这样就能保证同步数据的一致性,通常情况下都是指定为row,这样可以为数据库的恢复与同步带来更好的可靠性。

但是这种格式,需要更大的容量来记录,比较占用空间,恢复与同步时会更消耗IO资源,影响执行速度。

所以就有了一种折中的方案,指定为mixed,记录的内容是前两者的混合。

MySQL会判断这条SQL语句是否可能引起数据不一致,如果是,就用row格式,否则就用statement格式。

写入机制

binlog的写入时机也非常简单,事务执行过程中,先把日志写到binlog cache,事务提交的时候,再把binlog cache写到binlog文件中。

因为一个事务的binlog不能被拆开,无论这个事务多大,也要确保一次性写入,所以系统会给每个线程分配一个块内存作为binlog cache

我们可以通过binlog_cache_size参数控制单个线程 binlog cache 大小,如果存储内容超过了这个参数,就要暂存到磁盘(Swap)。

binlog日志刷盘流程如下

  • 上图的 write,是指把日志写入到文件系统的 page cache,并没有把数据持久化到磁盘,所以速度比较快
  • 上图的 fsync,才是将数据持久化到磁盘的操作

writefsync的时机,可以由参数sync_binlog控制,默认是0

0的时候,表示每次提交事务都只write,由系统自行判断什么时候执行fsync

虽然性能得到提升,但是机器宕机,page cache里面的 binlog 会丢失。

为了安全起见,可以设置为1,表示每次提交事务都会执行fsync,就如同 redo log 日志刷盘流程 一样。

最后还有一种折中方式,可以设置为N(N>1),表示每次提交事务都write,但累积N个事务后才fsync

在出现IO瓶颈的场景里,将sync_binlog设置成一个比较大的值,可以提升性能。

同样的,如果机器宕机,会丢失最近N个事务的binlog日志。

两阶段提交

redo log(重做日志)让InnoDB存储引擎拥有了崩溃恢复能力。

binlog(归档日志)保证了MySQL集群架构的数据一致性。

虽然它们都属于持久化的保证,但是侧重点不同。

在执行更新语句过程,会记录redo logbinlog两块日志,以基本的事务为单位,redo log在事务执行过程中可以不断写入,而binlog只有在提交事务时才写入,所以redo logbinlog的写入时机不一样。

回到正题,redo logbinlog两份日志之间的逻辑不一致,会出现什么问题?

我们以update语句为例,假设id=2的记录,字段c值是0,把字段c值更新成1SQL语句为update T set c=1 where id=2

假设执行过程中写完redo log日志后,binlog日志写期间发生了异常,会出现什么情况呢?

由于binlog没写完就异常,这时候binlog里面没有对应的修改记录。因此,之后用binlog日志恢复数据时,就会少这一次更新,恢复出来的这一行c值是0,而原库因为redo log日志恢复,这一行c值是1,最终数据不一致。

为了解决两份日志之间的逻辑一致问题,InnoDB存储引擎使用两阶段提交方案。

原理很简单,将redo log的写入拆成了两个步骤preparecommit,这就是两阶段提交

使用两阶段提交后,写入binlog时发生异常也不会有影响,因为MySQL根据redo log日志恢复数据时,发现redo log还处于prepare阶段,并且没有对应binlog日志,就会回滚该事务。

再看一个场景,redo log设置commit阶段发生异常,那会不会回滚事务呢?

并不会回滚事务,它会执行上图框住的逻辑,虽然redo log是处于prepare阶段,但是能通过事务id找到对应的binlog日志,所以MySQL认为是完整的,就会提交事务恢复数据。

undo log

我们知道如果想要保证事务的原子性,就需要在异常发生时,对已经执行的操作进行回滚,在 MySQL 中,恢复机制是通过 回滚日志(undo log) 实现的,所有事务进行的修改都会先记录到这个回滚日志中,然后再执行相关的操作。如果执行过程中遇到异常的话,我们直接利用 回滚日志 中的信息将数据回滚到修改之前的样子即可!并且,回滚日志会先于数据持久化到磁盘上。这样就保证了即使遇到数据库突然宕机等情况,当用户再次启动数据库的时候,数据库还能够通过查询回滚日志来回滚将之前未完成的事务。

另外,MVCC 的实现依赖于:隐藏字段、Read View、undo log。在内部实现中,InnoDB 通过数据行的 DB_TRX_ID 和 Read View 来判断数据的可见性,如不可见,则通过数据行的 DB_ROLL_PTR 找到 undo log 中的历史版本。每个事务读到的数据版本可能是不一样的,在同一个事务中,用户只能看到该事务创建 Read View 之前已经提交的修改和该事务本身做的修改

总结

MySQL InnoDB 引擎使用 redo log(重做日志) 保证事务的持久性,使用 undo log(回滚日志) 来保证事务的原子性

MySQL数据库的数据备份、主备、主主、主从都离不开binlog,需要依靠binlog来同步数据,保证数据一致性。

站在巨人的肩膀上

  • 《MySQL 实战 45 讲》
  • 《从零开始带你成为 MySQL 实战优化高手》
  • 《MySQL 是怎样运行的:从根儿上理解 MySQL》
  • 《MySQL 技术 Innodb 存储引擎》

MySQL 好文推荐

  • CURD 这么多年,你有了解过 MySQL 的架构设计吗?open in new window
  • 浅谈 MySQL InnoDB 的内存组件

转自:MySQL三大日志(binlog、redo log和undo log)详解 | JavaGuide 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【MySQL】MySQL进阶之路(六)MySQL三大日志(binlog、redo log和undo log)详解 的相关文章

  • MySQL 在同一列上两次连接同一个表,不同的值仅返回最近的行

    我一直试图解决复杂 JOIN 的一小部分 我们有一个 说明 表和一个 估计 表 在 估计 中 我们有不同类型的多行estimates对于给定的指令 说明表 id address status 1 27 TAYLOR ROAD ALBION
  • 将结果行强制转换为对象

    有没有办法将调用存储过程获得的结果行强制转换为特定对象 以便我可以仅将该对象的列表传递到视图中 我知道我可以使用 Node list 之类的东西来做到这一点 但我最终将用一个相当复杂的存储过程替换 getnodes 该存储过程创建临时表并执
  • PDO在mysql性能中的作用

    最近我在浏览一篇博客 注意到有关在mysql中使用PDO的一些要点 它改变了我对PDO的看法 要点是 本机准备好的语句无法利用查询缓存 从而导致性能降低 本机准备好的语句无法执行某些类型的查询 例如 SHOW TABLES 本机准备好的语句
  • 根据时间变量对两个表中的一对 COUNT 求和

    花了一个多小时的时间寻找这个问题的答案 但运气不佳 我有两个具有相同列名的区域表 我可以根据以下查询为任一表提供结果列表 将 Table2 替换为 Table1 SELECT Table1 YEAR FORMAT COUNT Table1
  • 在 SQL 查询中使用聚合函数

    我的表结构是这样的 ATT Table Fields Act ID Assigned To ID Percent Complete Integer value Act ID is primary key Assigned To ID is
  • 选择从查询中检索列名称的列

    我正在寻找一种优雅的方法来从表 A 中选择列 其中列名是从表 B 上的查询中检索的 对表 B 的查询结果 col01 表 A 有几个名为 col01 col02 col03 最终查询应该是为了结果 result from B effecti
  • 致命错误:无法在functions.php第25行中重新声明session_start()

    当我尝试让登录部分正常工作时遇到问题 我不断遇到的问题是 致命错误 无法在 public html login functions php 第 25 行重新声明 session start
  • 如何处理 pcntl_fork(): 错误 35?

    我有 php7 CLI 守护进程 它连续解析文件大小超过 50M 的 json 我试图使用 pcntl fork 的单独进程将每 1000 个解析数据条目保存到 mysql 对于约 200k 行 它工作得很好 然后我得到pcntl fork
  • Android:Json 无法从 mysql 数据库检索任何文件,它是空的

    我是 android 新手 我正在使用 mysql 数据库 其中我链接 php 文件进行连接 工作正常 但我的代码没有显示任何内容 它只显示背景色黑色 而不是显示数据库中的数据 public class HomeFragment exten
  • 如何创建具有两个主键和两个外键的表?

    create table Machine Machine ID int primary key Machine Name varchar 30 Machine Title varchar 30 create table Part Part
  • Laravel updateOrCreate 带有自动增量数据库

    我的目的是如果值存在则更新 否则插入新行提交表单后在数据库表中 问题是 这里的函数添加新列在数据库表中而不是更新它们 这是我的功能 MyModel updateOrCreate array myField gt myValue gt whe
  • mysql 部分索引、反向索引

    我有一张包含单词列表的表格 字 VARCHAR 16 我需要创建一个反向索引 IE 单词 apple 将索引为 elppa 单词 banana 将索引为 ananab 依此类推 另外 是否可以索引单词的一部分 例如 跳过第一个 最后一个或
  • 为什么Mysql在连接另一个表B时对表A使用全表扫描?

    我有一个表 A 和一个表 B 我正在从表 A 中选择信息 其中我只需要表 A 中的信息 其中表 B 联接表 A 我在联合列和 WHERE 子句上有索引 这是选择代码 SELECT FROM tableA INNER JOIN tableB
  • 使用 mysql_real_escape_string() 时出现访问被拒绝错误

    我试图在数据进入我的数据库之前转义一些数据 但我不断收到此错误 Warning mysql real escape string Access denied for user 现在 这通常表明我尚未连接到数据库 它还声明 使用密码 NO 我
  • 带有mysql的实体框架,linux和windows之间的表大小写问题

    我们目前正在开发一个使用 Code First Entity Framework 和 Mysql 的产品 开发数据库托管在 Windows 环境中 而生产 mysql 则托管在 Linux 环境中 我遇到的问题是 mysql 中的表命名如下
  • 如何使用 SQLAlchemy 进行“mysql 解释”

    我有一个像这样的sql DBSession query Model filter 我想用这个 sql 来解释SQLAlchemy 你想要将 SQLAlchemy 查询编译为字符串 https docs sqlalchemy org faq
  • 获取两个表中某段时间内SUM数量的数据

    我有下面两张表 订单详细信息 OrderNumber quantityordered priceEach Orders OrderNumber orderDate 问题 orderDate 看起来像这样 2005 01 01 2005 02
  • MySQL 监听通知等效项

    是否有相当于 PostgresQL 的notify http www postgresql org docs 9 1 static sql notify html and listen http www postgresql org doc
  • Ionic框架和php mysql

    我是 Ionic Apahce Cordova 的新手 我创建了一个简单的应用程序 它具有静态列表视图项 但我想从 MYSQL 表获取数据并将其替换到我的静态列表中 我用谷歌搜索了一下 有人在研究它 但我不知道应该把我的 php 文件放在哪
  • mysql连接3个表

    如何连接三个具有一个公共列 id 的mysql表 例如 从表1中选择a b 从表2中选择c d 从表3中选择e f 其中id x 谢谢 SELECT t1 a t1 b t2 c t2 d t3 e t3 f FROM table1 t1

随机推荐

  • Ubuntu开启SSH服务远程登录

    Ubuntu开启SSH服务远程登录 Ubuntu下开启ssh服务并能通过MobaXterm或者 Xshell进行远程登录 本人使用的是window10系统安装的MobaXterm window10系统安装MobaXterm可以参考 http
  • MongoDB

    一 MongoDB简介 1 集成简介 spring data mongodb提供了MongoTemplate与MongoRepository两种方式访问mongodb xff0c MongoRepository操作简单 xff0c Mong
  • 更改桌面壁纸_使用DeskSlide轻松更改桌面墙纸

    更改桌面壁纸 Looking to add some variety to your desktop instead of looking at the same wallpaper day in and day out Have fun
  • 科学素养题(2022年2月-2022年10月)

    二月科学素养 在我国山东省和山西省中间的 山 34 是 C A泰山 B吕梁山 C太行山 D沂蒙山 在一些寻宝游戏中 每个线索都会指向下一个线索的位置 玩家可以顺着这些线索一个一个找到所有的元素 这样的寻宝游戏的设计与 数据结构有着异曲同工之
  • Servlet综合练习:个人博客系统

    功能简介 1 注册新用户 2 xff09 登录已有用户 3 xff09 展示博客列表 xff08 包含文章标题以及作者信息 xff09 xff0c 点击标题就会跳转到文章详情页 4 xff09 文章详情页中 xff0c 显示文章标题 xff
  • Linux 环境搭建(如何获得一个免费云服务器)以及Linux基本指令

    搭建 Linux 环境 Linux 环境的搭建方式 主要有三种 直接安装在物理机上 但是由于 Linux 桌面使用起来非常不友好 不推荐 使用虚拟机软件 将 Linux 搭建在虚拟机上 但是由于当前的虚拟机软件 如 VMWare 之类的 存
  • 深入理解HTTP协议

    目标 xff1a 掌握 http 原理 xff0c 重点掌握 http Request amp Response 格式掌握 http 中相关重点知识 xff0c 如请求方法 xff0c 属性 xff0c 状态码等使用 java socket
  • 异常声音检测MFCC/HMM...相关

    有无研究这个方向的同学 xff0c 自己准备做这个方向 xff0c 可以相互讨论讨论 xff0c 留言我加你 xff0c 一起啊 x1f60f xff01
  • C语言goto语句简单使用

    简单介绍 C语言中提供了可以随意滥用的 goto语句和标记跳转的标号 从理论上 goto语句是没有必要的 xff0c 实践中没有goto语句也可以很容易的写出代码 但是某些场合下goto语句还是用得着的 xff0c 最常见的用法就是终止程序
  • 【网络原理】一个数据包从发送到接收在网络中经历了那些过程(详细分析)

    一个数据包从发送到接收在网络中经历了那些过程 假设学生给老师发送电子邮件 xff0c 内容为 xff1a 老师您好 xff01 从计算机A向另一台计算机B发送电子邮件 xff0c 站在网络原理的角度来分析整个过程 启动应用程序新建邮件 xf
  • 【贪心算法】leetcode402.移掉K位数字

    题目描述 xff08 传送门 xff09 给定一个以字符串表示的非负整数 num xff0c 移除这个数中的 k 位数字 xff0c 使得剩下的数字最小 注意 num 的长度小于 10002 且 k num 不会包含任何前导零 示例 1 输
  • 【Java项目实战】在线音乐播放器(从需求到产品完整解析)

    准备工作必看 xff1a Java项目实战 在线音乐播放器 xff08 前期准备 xff09 核心功能 登录 注册上传音乐删除某一个音乐信息删除选中的音乐信息查询音乐 包含查找指定 模糊匹配的音乐 添加音乐到 喜欢列表 查询喜欢的音乐 包含
  • MATLAB柱状图(数据可视化)

    示例 A 61 60 689 87 714 143 1 267 9515 C 61 127 5 160 4 231 9 400 2 B 61 C A D 61 A B C bar1 61 bar 2 5 17 A 39 BarWidth 3
  • ubuntu安装lxde_如何在Ubuntu上安装轻量级LXDE桌面

    ubuntu安装lxde LXDE is a lightweight desktop alternative to Unity GNOME and KDE It s ideal for old computers or anyone loo
  • 【leecode每日一题】636. 函数的独占时间

    题目描述 xff08 链接 xff09 有一个 单线程 CPU 正在运行一个含有 n 道函数的程序 每道函数都有一个位于 0 和 n 1 之间的唯一标识符 函数调用 存储在一个 调用栈 上 xff1a 当一个函数调用开始时 xff0c 它的
  • 【正则表达式】一、常见符号含义

    正则表达式入门 常见字符含义 常见字符含义 ABC 匹配目标字符串中 内的字符 span class token keyword import span re span class token keyword if span name sp
  • Java中文件相对路径和绝对路径的用法(IO同样适用),系统找不到指定文件的解决办法讲解

    引入场景 xff1a 在我们日常开发中 xff0c 经常需要去读取文件的内容 xff0c 但经常出现文件未发现的问题 xff0c 如下图 xff1a 出现这个问题的原因就有二个 xff0c 第一是文件真的不存在 xff0c 第二就是文件明明
  • Java中的Collections类[80]

    Java中的Collections类 80 文章目录 Java中的Collections类 80 前言一 Collections基础使用二 查找与替换三 同步控制 线程安全 四 设置不可变集合五 其他方法六 小结 前言 本章将继续使用代码加
  • Ubuntu20.04修复Chrome提示缺少依赖项问题

    这两天正好需要用服务器跑一个脚本需要用到chrome xff0c 本打算在服务器上安装chrome xff0c 从官方那边下载完deb包 xff0c 安装的时候报了这个错 xff1a dpkg error processing packag
  • 【MySQL】MySQL进阶之路(六)MySQL三大日志(binlog、redo log和undo log)详解

    写在前面的话 脑子是个好东西 xff0c 可惜的是一直没有搞懂脑子的内存删除机制是什么 xff0c 所以啊 xff0c 入行多年 xff0c 零零散散的文章看了无数 xff0c 却总是学习了很多也忘了很多 痛定思痛的我决定从今天开始系统的梳