美团二面:如何解决 bin log 与 redo log 的一致性问题

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 打开百度就像吃饭一样自然

刚看见这个题目的时候还是有点懵逼的,后来才反应过来其实问的就是 redo log 的两阶段提交

老规矩,背诵版在文末。

为什么说 redo log 具有崩溃恢复的能力

前面我们说过,MySQL Server 层拥有的 bin log 只能用于归档,不足以实现崩溃恢复(crash-safe),需要借助 InnoDB 引擎的 redo log 才能拥有崩溃恢复的能力。所谓崩溃恢复就是:即使在数据库宕机的情况下,也不会出现操作一半的情况(更直白点说:日志已经写好,但是数据并未落盘,通过日志来把数据恢复到磁盘上

至于为什么说 redo log 具有崩溃恢复的能力,而 bin log 没有,我们先来简单看一下这两种日志有哪些不同点:

1)适用对象不同

  • bin log 是 MySQL 的 Server 层实现的,所有引擎都可以使用
  • 而 redo log 是 InnoDB 引擎特有的

2)写入内容不同

  • bin log 是逻辑日志,记录的是这个语句的原始逻辑,比如 “给 id = 1 这一行的 age 字段加 1”
  • redo log 是物理日志,记录的是 “在某个数据页上做了什么修改”

3)写入方式不同

  • bin log 是可以追加写入的。“追加写” 是指 bin log 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志
  • redo log 是循环写的,空间固定会被用完

可以看到,redo log 和 bin log 的一个很大的区别就是,一个是循环写,一个是追加写。也就是说 redo log 只会记录未刷入磁盘的日志,已经刷入磁盘的数据都会从 redo log 这个有限大小的日志文件里删除。

而 bin log 是追加日志,保存的是全量的日志。这就会导致一个问题,那就是没有标志能让 InnoDB 从 bin log 中判断哪些数据已经刷入磁盘了,哪些数据还没有。

举个例子,bin log 记录了两条日志:

记录 1:给 id = 1 这一行的 age 字段加 1
记录 2:给 id = 1 这一行的 age 字段加 1

假设在记录 1 刷盘后,记录 2 未刷盘时,数据库崩溃。重启后,只通过 bin log 数据库是无法判断这两条记录哪条已经写入磁盘,哪条没有写入磁盘,不管是两条都恢复至内存,还是都不恢复,对 id = 1 这行数据来说,都是不对的。

但 redo log 不一样,只要刷入磁盘的数据,都会从 redo log 中被抹掉,数据库重启后,直接把 redo log 中的数据都恢复至内存就可以了。

这就是为什么说 redo log 具有崩溃恢复的能力,而 bin log 不具备。

直接用 binlog 做全量恢复也能做崩溃后的数据恢复,类比主从同步,但是速度肯定很慢,一般都是用 redolog 做崩溃恢复,binlog 做数据同步

redo log 两阶段提交

前面我们介绍过一条 SQL 查询语句的执行过程,简单回顾:

  1. MySQL 客户端与服务器间建立连接,客户端发送一条查询给服务器;
  2. 服务器先检查查询缓存,如果命中了缓存,则立刻返回存储在缓存中的结果;否则进入下一阶段;
  3. 服务器端进行 SQL 解析、预处理,生成合法的解析树;
  4. 再由优化器生成对应的执行计划;
  5. 执行器根据优化器生成的执行计划,调用相应的存储引擎的 API 来执行,并将执行结果返回给客户端

网络异常,图片无法展示
|

对于更新语句来说,这套流程同样也是要走一遍的,不同的是,更新流程还涉及两个重要的日志模块 bin log 和 redo log。

以下面这条简单的 SQL 语句为例,我们来解释下执行器和 InnoDB 存储引擎在更新时做了哪些事情:

update table set age = age + 1 where id = 1;
  1. 执行器:找存储引擎取到 id = 1 这一行记录
  2. 存储引擎:根据主键索引树找到这一行,如果 id = 1 这一行所在的数据页本来就在内存池(Buffer Pool)中,就直接返回给执行器;否则,需要先从磁盘读入内存池,然后再返回
  3. 执行器:拿到存储引擎返回的行记录,把 age 字段加上 1,得到一行新的记录,然后再调用存储引擎的接口写入这行新记录
  4. 存储引擎:将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,为 redo log 中的事务打上 prepare 标识。然后告知执行器执行完成了,随时可以提交事务

注意不要把这里的提交事务和我们 sql 语句中的提交事务 commit 命令搞混了哈,我们这里说的提交事务,指的是事务提交过程中的一个小步骤,也是最后一步。当这个步骤执行完成后,commit 命令就执行成功了。

  1. 执行器:生成这个操作的 bin log,并把 bin log 写入磁盘
  2. 执行器:调用存储引擎的提交事务接口
  3. 存储引擎:把刚刚写入的 redo log 状态改成提交(commit)状态,更新完成

如下图所示:

网络异常,图片无法展示
|

图片中的写入 redo log 和写入 bin log,并不等同于写入磁盘文件了哦,redo log 和 bin log 的写入其实分为两个阶段,可能仅仅写入内存了,也可能继续持久化到磁盘了,这个下篇文章有详细解释~

可以看到,所谓两阶段提交,其实就是把 redo log 的写入拆分成了两个步骤:prepare 和 commit

所以,为什么要这样设计呢?这样设计怎么就能够实现崩溃恢复呢?

<br>

根据两阶段提交,崩溃恢复时的判断规则是这样的:

  1. 如果 redo log 里面的事务是完整的,也就是已经有了 commit 标识,则直接提交
  2. 如果 redo log 里面的事务处于 prepare 状态,则判断对应的事务 binlog 是否存在并完整
  • a. 如果 binlog 存在并完整,则提交事务;
  • b. 否则,回滚事务。

当然,这样说小伙伴们肯定没法理解,下面来看几个实际的例子:

如下图所示,假设数据库在写入 redo log(prepare) 阶段之后、写入 binlog 之前,发生了崩溃,此时 redo log 里面的事务处于 prepare 状态,binlog 还没写(对应 2b),所以崩溃的时候,这个事务会回滚。

Why?

因为 binlog 还没有写入,之后从库进行同步的时候,无法执行这个操作,那如果我们主库上继续恢复这个操作的话显然就会导致主备不一致,所以在主库上需要回滚这个事务

并且,由于 binlog 还没写,所以也就不会传到备库,从而避免主备不一致的情况。

网络异常,图片无法展示
|

而如果数据库在写入 binlog 之后,redo log 状态修改为 commit 前发生崩溃,此时 redo log 里面的事务仍然是 prepare 状态,binlog 存在并完整(对应 2a),所以如果在这个时刻数据库崩溃了,该事务在崩溃恢复的时候会被正常提交。

Why?

因为 binlog 已经写入成功了,这样之后就会被从库同步过去,但是如果 redolog 没有这条记录的话,所以为了主备一致,在主库上崩溃恢复的时候需要提交这个事务。

网络异常,图片无法展示
|

所以,其实可以看出来,处于 prepare 阶段的 redo log 加上完整的 bin log,就能保证数据库的崩溃恢复了

可能有同学就会问了,MySQL 咋知道 bin log 是不是完整的?

简单来说,一个事务的 binlog 是有完整格式的:

  • statement 格式的 bin log,最后会有 COMMIT
  • row 格式的 bin log,最后会有 XID event

而对于 bin log 可能会在中间出错的情况,MySQL 5.6.2 版本以后引入了 binlog-checksum 参数,用来验证 bin log 内容的正确性。

思考一个问题,两阶段提交是必要的吗?可不可以先 redo log 写完,再写 bin log 或者反过来?

1)对于先写完 redo log 后写 bin log 的情况:

假设在 redo log 写完,bin log 还没有写完的时候,MySQL 崩溃。崩溃恢复的时候主库会根据 redolog 执行更新,但是这时候 binlog 里面并没有记录这个语句。因此,从库同步的时候,就会丢失这个更新,和主库不一致。

网络异常,图片无法展示
|

2)对于先写完 binlog 后写 redo log 的情况:

如果在 bin log 写完,redo log 还没写的时候,MySQL 崩溃。因为 binlog 已经写入成功了,这样之后就会被从库同步过去,但是实际上 redo log 还没写,崩溃恢复的时候主库并不会执行这个事务,所以从库相比主库就会多执行一个事务,导致主备不一致

网络异常,图片无法展示
|

⭐ 所以,总结来说,不论 MySQL 什么时刻发生崩溃,最终是 commit 还是 rollback 完全取决于 MySQL 能不能判断出 binlog 和 redolog 在逻辑上是否达成了一致。只要逻辑上达成了一致就可以 commit,否则只能rollback。


最后放上这道题的背诵版:

🥸 面试官

  • 问法 1:如何解决 bin log 与 redo log 的一致性问题?
  • 问法 2:一条 SQL 更新语句是如何执行的?
  • 问法 3:讲一下 redo log / redo log 两阶段提交原理

😎 小牛肉

所谓两阶段提交,其实就是把 redo log 的写入拆分成了两个步骤:prepare 和 commit。

首先,存储引擎将执行更新好的新数据存到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务

然后执行器生成这个操作的 bin log,并把 bin log 写入磁盘

最后执行器调用存储引擎的提交事务接口,存储引擎把刚刚写入的 redo log 状态改成提交(commit)状态,更新完成

如果数据库在写入 redo log(prepare) 阶段之后、写入 binlog 之前,发生了崩溃:

此时 redo log 里面的事务处于 prepare 状态,binlog 还没写,之后从库进行同步的时候,无法执行这个事务,那如果我们主库上继续恢复执行这个事务的话显然就会导致主备不一致,所以崩溃恢复的时候在主库上需要回滚这个事务

而如果数据库在写入 binlog 之后,redolog 状态修改为 commit 前发生崩溃,此时 redolog 里面的事务仍然是 prepare 状态,binlog 存在并完整,这样之后就会被从库同步过去,所以为了主备一致,在主库上崩溃恢复的时候需要提交这个事务。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
3天前
|
安全 关系型数据库 MySQL
MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!
《MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!》介绍了MySQL中的三种关键日志:二进制日志(Binary Log)、重做日志(Redo Log)和撤销日志(Undo Log)。这些日志确保了数据库的ACID特性,即原子性、一致性、隔离性和持久性。Redo Log记录数据页的物理修改,保证事务持久性;Undo Log记录事务的逆操作,支持回滚和多版本并发控制(MVCC)。文章还详细对比了InnoDB和MyISAM存储引擎在事务支持、锁定机制、并发性等方面的差异,强调了InnoDB在高并发和事务处理中的优势。通过这些机制,MySQL能够在事务执行、崩溃和恢复过程中保持
19 3
|
20天前
|
SQL 存储 缓存
MySQL进阶突击系列(02)一条更新SQL执行过程 | 讲透undoLog、redoLog、binLog日志三宝
本文详细介绍了MySQL中update SQL执行过程涉及的undoLog、redoLog和binLog三种日志的作用及其工作原理,包括它们如何确保数据的一致性和完整性,以及在事务提交过程中各自的角色。同时,文章还探讨了这些日志在故障恢复中的重要性,强调了合理配置相关参数对于提高系统稳定性的必要性。
|
1月前
|
关系型数据库 MySQL 数据库
【赵渝强老师】MySQL的binlog日志文件
MySQL的binlog日志记录了所有对数据库的更改操作(不包括SELECT和SHOW),主要用于主从复制和数据恢复。binlog有三种模式,可通过设置binlog_format参数选择。示例展示了如何启用binlog、设置格式、查看日志文件及记录的信息。
123 6
|
2月前
|
SQL 存储 关系型数据库
美团面试:binlog、redo log、undo log的底层原理是什么?它们分别实现ACID的哪个特性?
老架构师尼恩在其读者交流群中分享了关于 MySQL 中 redo log、undo log 和 binlog 的面试题及其答案。这些问题涵盖了事务的 ACID 特性、日志的一致性问题、SQL 语句的执行流程等。尼恩详细解释了这些日志的作用、所在架构层级、日志形式、缓存机制以及写文件方式等内容。他还提供了多个面试题的详细解答,帮助读者系统化地掌握这些知识点,提升面试表现。此外,尼恩还推荐了《尼恩Java面试宝典PDF》和其他技术圣经系列PDF,帮助读者进一步巩固知识,实现“offer自由”。
美团面试:binlog、redo log、undo log的底层原理是什么?它们分别实现ACID的哪个特性?
|
2月前
|
存储 关系型数据库 MySQL
MySQL中的Redo Log、Undo Log和Binlog:深入解析
【10月更文挑战第21天】在数据库管理系统中,日志是保障数据一致性和完整性的关键机制。MySQL作为一种广泛使用的关系型数据库管理系统,提供了多种日志类型来满足不同的需求。本文将详细介绍MySQL中的Redo Log、Undo Log和Binlog,从背景、业务场景、功能、底层实现原理、使用措施等方面进行详细分析,并通过Java代码示例展示如何与这些日志进行交互。
244 0
|
1月前
|
XML 安全 Java
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
本文介绍了Java日志框架的基本概念和使用方法,重点讨论了SLF4J、Log4j、Logback和Log4j2之间的关系及其性能对比。SLF4J作为一个日志抽象层,允许开发者使用统一的日志接口,而Log4j、Logback和Log4j2则是具体的日志实现框架。Log4j2在性能上优于Logback,推荐在新项目中使用。文章还详细说明了如何在Spring Boot项目中配置Log4j2和Logback,以及如何使用Lombok简化日志记录。最后,提供了一些日志配置的最佳实践,包括滚动日志、统一日志格式和提高日志性能的方法。
345 30
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
|
14天前
|
监控 安全 Apache
什么是Apache日志?为什么Apache日志分析很重要?
Apache是全球广泛使用的Web服务器软件,支持超过30%的活跃网站。它通过接收和处理HTTP请求,与后端服务器通信,返回响应并记录日志,确保网页请求的快速准确处理。Apache日志分为访问日志和错误日志,对提升用户体验、保障安全及优化性能至关重要。EventLog Analyzer等工具可有效管理和分析这些日志,增强Web服务的安全性和可靠性。
|
2月前
|
XML JSON Java
Logback 与 log4j2 性能对比:谁才是日志框架的性能王者?
【10月更文挑战第5天】在Java开发中,日志框架是不可或缺的工具,它们帮助我们记录系统运行时的信息、警告和错误,对于开发人员来说至关重要。在众多日志框架中,Logback和log4j2以其卓越的性能和丰富的功能脱颖而出,成为开发者们的首选。本文将深入探讨Logback与log4j2在性能方面的对比,通过详细的分析和实例,帮助大家理解两者之间的性能差异,以便在实际项目中做出更明智的选择。
333 3
|
24天前
|
存储 监控 安全
什么是事件日志管理系统?事件日志管理系统有哪些用处?
事件日志管理系统是IT安全的重要工具,用于集中收集、分析和解释来自组织IT基础设施各组件的事件日志,如防火墙、路由器、交换机等,帮助提升网络安全、实现主动威胁检测和促进合规性。系统支持多种日志类型,包括Windows事件日志、Syslog日志和应用程序日志,通过实时监测、告警及可视化分析,为企业提供强大的安全保障。然而,实施过程中也面临数据量大、日志管理和分析复杂等挑战。EventLog Analyzer作为一款高效工具,不仅提供实时监测与告警、可视化分析和报告功能,还支持多种合规性报告,帮助企业克服挑战,提升网络安全水平。
|
2月前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1710 14