MySQL 学习(七)undo log、redo log、bin log 的作用以及持久化机制
目录
- 一、前言
- 二、三大日志的概念、作用、存储位置
- 2.1 bin log 二进制执行日志
- 2.2 undo log 事务回滚日志
- 2.3 redo log 快速恢复日志
- 三、补充说明
- 3.1 补充:为什么使用 buffer pool 而不直接修改磁盘中的数据?
- 3.2 补充:同为操作数据变更的日志,有了 bin log 为什么还要 redo log?
- 3.3 补充:redo log 一定能保证事务的持久性吗?
- 四、redo log 与 undo log 的持久化机制
- 3.1 redo log 持久化
- 3.2 undo log 持久化

一、前言
你有没有想过,MySQL是怎么保证数据安全的?比如突然断电时,为什么你的订单数据不会丢失?其实这全靠三个"小帮手":undo log
、redo log
和 bin log
。
undo log
就像游戏的"存档点",让你能 回滚 错误操作;redo log
是应急的"记事本",断电时能 快速恢复 数据;bin log
则是完整的"操作日记",用于 数据备份 和 同步。
这篇文章我们就来聊聊这三个日志的工作原理,看看它们是如何默契配合,守护你的数据安全的。
二、三大日志的概念、作用、存储位置
2.1 bin log 二进制执行日志
bin log 的基本概念:
binlog
是一个二进制格式的文件,记录了对 MySQL 数据库执行更改的所有 写操作,例如更改数据库表和更改内容的 SQL 语句都会记录到 binlog 里,但是不会记录 SELECT
和 SHOW
这类操作。
- binlog 在 MySQL 的 Server 层实现(引擎共用)
- binlog 为逻辑日志,记录的是一条 SQL 语句的原始逻辑。
- binlog 不限制大小,追加写入,不会覆盖以前的日志。
- 默认情况下,binlog 日志是二进制的,不能使用查看文本工具的命令(比如:cat、vi 等)查看,而是用
mysqlbinlog
解析查看。
bin log 的作用:
- 主从复制: 从主库开启 Binlog 功能,这样主库就可以把 Binlog 传递给从库,从库拿到 Binlog 后实现数据恢复达到主从数据一致性。
- 数据恢复: 通过 mysqlbinlog 工具来恢复数据。
bin log 文件存储位置:
首先,需要确保binlog开启:
SHOW VARIABLES LIKE 'log_bin';
- MySQL 5.7需手动开启binlog;
- MySQL 8.0默认已开启binlog,只需确认配置。
binlog文件默认在 datadir
目录下:
-- 查看数据目录位置
show variables like 'datadir';
如果手动指定了位置,则需要到 my.cnf 或 my.ini 中进行查看:
# 指定了目录和前缀
log_bin = D:/java/mysql-5.7.33-winx64/binlog/mysql-bin
undo log 文件存储位置
2.2 undo log 事务回滚日志
undo log 基本概念:
undo log
是一种用于撤销回退的日志,在数据库事务开始之前,MySQL 会先记录更新前的数据到 undo log 日志文件里面,当事务回滚时或者数据库崩溃时,可以利用 undo log 来进行回退。
undo log 的产生和销毁:
- undo log 在事务开始前产生;事务在提交时,并不会立刻删除 undo log,innoDB 会将该事务对应的 undo log 放入到删除列表中,后面会通过后台线程
puge thread
进行回收处理。
注意: undo log 也会产生 redo log,因为 undo log 也要实现持久性保护。
undo log 的作用:
-
提供回滚操作【undo log 实现事务的原子性】
在数据修改的时候,不仅记录了 redo log,还记录相对应的 undo log,如果因为某些原因导致事务执行失败了,可以借助 undo log 进行回滚。
-
提供多版本控制(MVCC)【undo log 实现多版本并发控制(MVCC)】
MVCC,即多版本控制。在 MySQL 数据库 InnoDB 存储引擎中,用 undo log 来实现多版本并发控制(MVCC)。当读取的某一行被其他事务锁定时,它可以从 undo log 中分析出该行记录以前的数据版本是怎样的,从而让用户能够读取到当前事务操作之前的数据【快照读】。
undo log 的存储位置:
可以使用如下命令查看存储位置:
SHOW VARIABLES LIKE 'innodb_undo%';
从下面执行结果可以看到,这里的相对位置是以数据存储位置 datadir
为准。
我们执行如下命令查看 datadir
数据位置:
-- 查看数据目录位置
show variables like 'datadir';
undo log 的日志文件如下:
(补充:ib是InnoDB的简写)
2.3 redo log 快速恢复日志
redo log 基本概念:
redo log
被称作重做日志,包括两部分:
- 一个是内存中的日志缓存
redo log buffer
; - 另一个是磁盘上的日志文件
redo log file
。
补充: 在内存层面的日志缓存会按照固定大小进行存储,默认 16M,通过 innodb_log_buffer_size 参数可以修改。
MySQL 每执行一条 DML 语句,先将记录写入 redo log buffer,后续某个时间点再一次性将多个操作记录写到 redo file log。
MySQL 中 InnoDB 修改数据的操作流程:
- 先将原始数据从磁盘中读入内存中来,修改数据的内存拷贝,产生脏数据
- 生成一条重做日志并写入redo log buffer,记录的是数据被修改后的值
- 默认在事务提交后将redo log buffer中的内容刷新到redo log file,对redo log file采用追加写的方式
- 定期将内存中修改的数据刷新到磁盘中(这里说的是那些还没及时被后台线程刷盘的脏数据)
通常所说的 Write Ahead Log(预写式技术)指的是 在持久化一个数据页之前,先将内存中相应的日志页持久化。这种技术可以大大减少 IO 操作的频率,提升数据刷新的效率。
redo log 的作用:
- mysql 每执行一条 DML 语句,先将记录写入 redo log buffer。后续某个时间点再一次性将多个操作记录写道 redo log file。当故障发生致使内存数据丢失后,InnoDB 会在重启时,经过重放 redo log,将 Page 恢复到崩溃之前的状态 通过 Redo log 可以实现事务的持久性。
redo log 的存储位置:
redo log 默认存储在 datadir
数据目录下的 ib_logfile0
和 ib_logfile1
文件中,如下所示:
(补充:ib是InnoDB的简写)
三、补充说明
相信大家看了上面的概念一定云里雾里,脑袋里有很多疑问,下面就为大家答疑一下。
3.1 补充:为什么使用 buffer pool 而不直接修改磁盘中的数据?
因为直接修改磁盘数据的话,它是 随机IO,修改的数据分布在磁盘中的不同位置,需要来回的查找,所以命中率低,消耗打,而一个小小的修改就不得不将整个页刷新到磁盘,利用率低。
与之相对的是 顺序IO,磁盘的数据分布在磁盘的一块,所以省去了查找的过程,节省在磁盘上的寻道事件。
使用后台线程以一定的频率去刷新磁盘可以降低随机IO的频率,增加吞吐量,这是使用 buffer pool 的根本原因。
3.2 补充:同为操作数据变更的日志,有了 bin log 为什么还要 redo log?
最核心的一点就是 两者记录的数据变更粒度是不一样 的。
以修改数据为例:
- binlog 是以 表 为记录主体,在 ROW 模式下,binlog 保存的表中每行的变更记录。
- 由于 MySQL 是以页为单位进行刷盘的,每一页的数据单位为 16K,所以在刷盘的过程中需要把数据刷新到磁盘的多个扇区中去。而把 16K 数据刷到磁盘的每个扇区里这个过程是 无法保证原子性的,如果数据库宕机,那么就可能会造成一部分数据成功过,而一部分数据失败的情况。而通过 binlog 这种级别的日志是无法恢复的,因为一个 update 可能更改了多个磁盘区域的数据,所以这个时候得需要通过 redo log 这种 记录到磁盘数据级别 的日志进行数据恢复。
redo log 和 binlog 的具体区别如下:
redo log | binlog | |
---|---|---|
文件大小 | redo log 的大小是固定的。 | binlog 可通过配置参数 max_binlog_size 设置每个 binlog 文件的大小。 |
实现方式 | redo log 是 InnoDB 引擎实现的,并不是所有引擎都有。 | binlog 是 Server 层实现的,所有引擎都可以使用 binlog 日志。 |
记录方式 | redo log 采用循环写的方式记录,当写到结尾时,会回到开头循环写日志。 | binlog 通过朱家的方式记录,当文件大小大于给定值后,后续的日志会记录到新的文件上。 |
使用场合 | redo log 用于崩溃恢复(crash-safe)。 | binlog 用于主从复制中数据恢复、时间点恢复。 |
由以上两者的对比可知:binlog 日志只用于归档,只依靠 binlog 是没有 crash-safe 能力的。
同样只有 redo log 也不行,因为 redo log 是 InnoDB 特有的,且日志上的记录落盘后会被覆盖掉。因此需要 binlog 和 redo log 二者同时记录,才能保证当数据库发生宕机重启时,数据不会丢失。(redo log 自动恢复,binlog 手动触发)
3.3 补充:redo log 一定能保证事务的持久性吗?
不一定,这样根据 redo log 的刷盘策略决定,因为 redo log buffer 同样是在内存中,如果提交事务之后,redo log buffer 还没来得及将数据刷新到 redo log file 中进行持久化,此时发生宕机照样会丢失数据。
那该如何解决呢?可以配置写入策略为实时写、实时刷。
-- 确保每次事务提交都刷盘 (默认值)
SET GLOBAL innodb_flush_log_at_trx_commit = 1;-- 使用 O_DIRECT 方式绕过OS缓存 (Linux)
SET GLOBAL innodb_flush_method = O_DIRECT;
四、redo log 与 undo log 的持久化机制
3.1 redo log 持久化
在计算机操作系统中,用户空间(user space) 下的缓冲区数据一般情况下是无法直接写入磁盘的,中间必须经过操作系统的 **内核控件(kernel space) ** 中的 缓冲区(OS Buffer)。
因此,redo log buffer 写入 redo log file 实际上是先写入 OS Buffer,然后再通过系统调用 fsync()
将其刷到 redo log file 中,过程如下:
Redo Buffer 持久化到 redo log 的策略,可以通过 innodb_flush_log_at_trx_commit
设置:
参数值 | 含义 |
---|---|
0 (延迟写) | 事务提交时不会将 redo log buffer 中日志写入到 os buffer ,而是每秒写入 os buffer 并调用 fsync() 写入到 redo log file 。也就是说设置为 0 时是(大约)每秒刷新写入到磁盘中的,当系统崩溃,会丢失1秒钟的数据。 |
1 (实时写,实时读) | 事务每次提交都会将 redo log buffer 中的日志写入到 os buffer 并调用 fsync() 刷到 redo log file 中。这种方式即使系统崩溃也不会丢失任何数据,但是因为每次提交都写入磁盘,IO 的性能较差。 |
2 (实时写,延迟刷) | 每次提交仅写入到 os buffer ,然后每秒调用 fsync() 将 os buffer 中的日志写入到 redo log file 。 |
一般建议选择取值 2
,因为 MySQL 挂了数据没有丢失,整个服务器挂了才会损失 1 秒的事务提交数据。
3.2 undo log 持久化
MySQL 中的 Undo Log 严格的讲不是Log,而是数据,因此它的管理和罗盘跟数据是一样的。
- Undo 的磁盘结构并不是顺序的,而是像数据一样按 Page 管理。
- Undo 写入时,也想数据一样产生对应的 Redo Log(因为 undo 也是对页面的修改,记录 undo 这个操作本身也会有对应的 redo)。
- Undo 的 Page 也像数据一样缓存在 Buffer Pool 中,跟数据 Page 一起做 LRU 换入换出,以及刷脏。Undo Page 的刷脏也像数据一样要等到对应的 Redo Log 落盘之后。
当事务提交的时候,innodb 不会立即删除 undo log,因为后续还可能会用到 undo log,如隔离级别为 repeatable read 时,事务读取的都是开启时的最新提交行版本,只要该事务不结束,该行版本就不能删除,即 undo log 不能删除。
但是在事务提交的时候,会将该事务对应的 undo log 放入到删除列表中,未来通过 purge
来删除。并且提交事务时,还会判断 undo log 分配的页是否可以宠用,如果可以重用,则会分配给后面来的事务,避免为每个独立的事务分配独立的 undo log 页而浪费存储空间和性能。
整理完毕,完结撒花~🌻
参考地址:
1.MySQL面试 | undo log、redo log、 bin log的作用是什么?https://www.bilibili.com/video/BV1Q4AkeKEai
2.硬核干货!一文掌握MySQL核心日志:binlog、redo log、undo log,https://baijiahao.baidu.com/s?id=1821334254098820619&wfr=spider&for=pc