binlog、redolog、undolog的区别和作用
我们知道事务有ACID四个,四个事务的实现是通过InnoDB日志和锁来保证的。
隔离性 主要通过数据库的锁机制(如行锁、表锁、间隙锁等)和 MVCC(多版本并发控制)来实现。
持久性 通过 redo log(重做日志)来实现
原子性 通过 undo log(回滚日志)来实现。
一致性:是事务的最终目标,由其他三个特性共同保证。
# 1. bin log
binlog 用于记录数据库执行的写入性操作(不包括查询)信息,以二进制的形式保存在磁盘中。binlog 是 mysql的逻辑日志,并且由 Server 层进行记录,使用任何存储引擎的 mysql 数据库都会记录 binlog 日志。
所以binlog日志并不是innodb独有的,它是server层的日志
既然是server层的日志,它记录的都是事务操作内容,是一种逻辑日志。
逻辑日志:可以简单理解为记录的就是sql语句 。
binlog 是通过追加的方式进行写入的,可以通过max_binlog_size 参数设置每个 binlog文件的大小,当文件大小达到给定值之后,会生成新的文件来保存日志。
# binlog使用场景:
binlog可以作为恢复数据使用,通过使用mysqlbinlog工具来恢复数据。- 主从复制搭建(数据同步)。在
Master端开启binlog,然后将binlog发送到各个Slave端,Slave端重放binlog从而达到主从数据一致
# binlog刷盘时机:
MySQL数据库中的任何存储引擎对于数据库的更改都会产生binlog,此时记录还在内存中,那么 biglog是什么时候刷到磁盘中的呢?
- 0:不去强制要求,由系统自行判断何时写入磁盘;
- 1:每次
commit的时候都要将binlog写入磁盘; - N:每N个事务,才会将
binlog写入磁盘。
从上面可以看出, sync_binlog 最安全的是设置是 1 ,这也是MySQL 5.7.7之后版本的默认值。但是设置一个大一些的值可以提升数据库性能,因此实际情况下也可以将值适当调大,牺牲一定的一致性来获取更好的性能。
# binlog日志格式:
binlog 日志有三种格式,分别为 STATMENT 、 ROW 和 MIXED。
在
MySQL 5.7.7之前,默认的格式是STATEMENT,MySQL 5.7.7之后,默认值是ROW。日志格式通过binlog-format指定。
STATMENT:基于SQL语句的复制(statement-based replication, SBR),每一条会修改数据的sql语句会记录到binlog中 。- 优点:不需要记录每一行的变化,减少了 binlog 日志量,节约了 IO , 从而提高了性能;
- 缺点:在某些情况下会导致主从数据不一致,比如执行sysdate() 、 slepp() 等 。
ROW:基于行的复制(row-based replication, RBR),不记录每条sql语句的上下文信息,仅需记录哪条数据被修改了。- 优点:不会出现某些特定情况下的存储过程、或function、或trigger的调用和触发无法被正确复制的问题 ;
- 缺点:会产生大量的日志,尤其是
alter table的时候会让日志暴涨
MIXED:基于STATMENT和ROW两种模式的混合复制(mixed-based replication, MBR),一般的复制使用STATEMENT模式保存binlog,对于STATEMENT模式无法复制的操作使用ROW模式保存binlog
# 2. redo log
redo log 是 InnoDB 引擎特有的。redo log作为异常宕机或者介质故障后的数据恢复使用。所以这也是持久性的依赖,只要事务提交成功,数据库对已经修改的数据就被永久保存下来了。
# 有了binglog为什么还要有redolog?
主要原因如下:
- binlog会记录所有的日志,是关于一个事务的逻辑日志,而redolog只需要记录innodb引擎本身的日志
- innodb是以页为单位和磁盘进行交互的,如果一个事务只修改一个数据页里面的几个字节,那将整个数据页刷盘就很浪费资源了;或者一个事务可能涉及修改多个数据页,而且binlog是随机写,如果每次事务提交都刷盘,会极大影响数据库的性能。
- 确保事务的持久性,如果binlog在刷盘的时候宕机了,就真的丢失了(当然binlog可以每次都刷盘但是性能不好),redo log的落盘并不是随着事务的提交才写入的,而是在事务的执行过程中,便开始写入
redo log buffer中,所以在恢复的时候,redolog依然记录了宕机前的数据。
因此设计了redolog, 具体来说就是只记录事务对数据页做了哪些修改,这样就能完美地解决性能问题了(相对而言文件更小并且是顺序IO)。
# redo log 概念
redo log记录的是新数据的备份。在事务提交前,只要将redo log持久化即可,不需要将数据持久化。当系统崩溃时,虽然数据没有持久化,但是redo log已经持久化。系统可以根据redo log的内容,将所有数据恢复到最新的状态。 (从而达到持久性)
# redo log 工作过程
mysql 每执行一条 DML 语句,先将记录写入 redo log buffer,后续某个时间点再一次性将多个操作记录写到 redo log file。这种 先写日志,再写磁盘 的技术就是 MySQL
里经常说到的 WAL(Write-Ahead Logging) 技术。
在计算机操作系统中,用户空间( user space )下的缓冲区数据一般情况下是无法直接写入磁盘的,中间必须经过操作系统内核空间( kernel space )缓冲区( OS Buffer )。
因此, redo log buffer 写入 redo logfile 实际上是先写入 OS Buffer ,然后再通过系统调用 fsync() 将其刷到 redo log file
中,过程如下:

支持三种将 redo log buffer 写入 redo log file 的时机,可以通过 innodb_flush_log_at_trx_commit 参数配置:

由 binlog 和 redo log 的区别可知:binlog 日志只用于归档,只依靠 binlog 是没有 crash-safe 能力的。
但只有 redo log 也不行,因为 redo log 是 InnoDB特有的,且日志上的记录落盘后会被覆盖掉。因此需要 binlog和 redo log二者同时记录,才能保证当数据库发生宕机重启时,数据不会丢失。
# 3. undo log
undo log的原理很简单,为了满足事务的原子性,在操作任何数据之前,首先将数据备份到一个地方(这个存储数据备份的地方称为undo log)。然后进行数据的修改。如果出现了错误或者用户执行了ROLLBACK语句,系统可以利用undo log中的备份将数据恢复到事务开始之前的状态。
简单来说就是,比如你执行一条 INSERT 语句,就会对应一条DELETE 的 undo log ,所以发生错误或者回滚了,就能回滚到事务之前的状态了。
#
# 4. 场景假设
假设数据库里有一行数据:
id = 1, name = '张三'
现在执行一条SQL语句:
UPDATE user SET name = '李四' WHERE id = 1;
# 1. Undo Log 记录了“改之前的样子”
作用:为了能在你后悔时(回滚),把数据变回原来的样子。
记录内容:它只关心这一行数据变更前的状态。它会记录:
“这行数据(id=1)原来的名字是 ‘张三’。”
记录时机:在修改数据之前,就先把这个“旧值”记下来。
流程比喻: 你在改作文之前,先把原文复印一份存起来。如果改坏了,拿出复印件就能恢复原状。
# 2. Binlog 记录了“怎么改的”
作用:为了让别的数据库跟我保持一致(主从复制),或者以后查账(数据恢复)。
记录内容:它关心的是执行了什么样的操作。根据格式不同,它可能记录两种形式:
如果是 Statement 格式(逻辑SQL):
“刚才执行了一条SQL:
UPDATE user SET name = ‘李四’ WHERE id = 1;”如果是 Row 格式(行变更):
“主键 id=1 的那一行,name 字段从 ‘张三’ 改成了 ‘李四’。”
记录时机:事务提交时才记录。如果事务没成功,Binlog 里就没有这条记录。
流程比喻: 你把改好的作文交给老师(主库),老师先在草稿本上(Binlog)记下“某年某月某日,李华把作文改成了……”,然后再把这篇作文展示给全班同学(从库)看。
# 3. Redo Log — 记录“改之后的样子”(物理级别)
- 目的:崩溃恢复时用,已提交的事务如果数据还没刷到磁盘,就用它来重做。
- 内容:物理日志,记录“哪个数据页的哪个位置改成了什么值”。
- 记录时机:修改数据的同时记录(WAL,Write-Ahead Logging,日志先行),事务提交时必须刷盘。
记录内容示例:
“第 5 号数据页(page 5),第 1024 字节处(偏移量),写入了字符串 ‘李四’。”
注意:它不关心你执行的是什么 SQL,只关心磁盘上的数据块哪里被改动了。
# 整个执行过程的对比
结合 Redo Log,这三个日志在一条 UPDATE 语句执行时的分工是这样的:
- 准备阶段(执行SQL):
- Undo Log:先把
name = ‘张三’这个旧值记下来(以便将来回滚)。 - 然后修改内存中的数据:把内存里的
name改成‘李四’。 - Redo Log:把这个修改动作(在哪个数据页、哪个位置写入了‘李四’)记下来(以便崩溃后恢复)。
- Undo Log:先把
- 提交阶段(Commit):
- Redo Log:进入 Prepare 阶段(准备就绪)。
- Binlog:把整个修改操作记录成文件(写入磁盘)。
- Redo Log:进入 Commit 阶段(标记事务成功)。
- 最终:内存中的数据,会在合适的时机刷到磁盘。
# 总结例子中的区别
针对刚才那条 UPDATE 语句,三个日志分别记了什么?
Undo Log(为了回滚):
{id: 1, name: ‘张三’}(只记旧值)Redo Log(为了崩溃恢复):
{页号: 100, 偏移量: 50, 数据: ‘李四’}(只记新值的物理位置)Binlog(为了复制/恢复):
UPDATE user SET name = ‘李四’ WHERE id = 1;(记整个操作)
所以,虽然它们都在“记录”,但 Undo Log 是为了“撤销修改”,而 Binlog 是为了“重复执行”。
# 5. 总结
| 对比维度 | undo log(回滚日志) | redo log(重做日志) | binlog(二进制日志) |
|---|---|---|---|
| 日志层次 | InnoDB 存储引擎层 | InnoDB 存储引擎层 | MySQL Server 层 |
| 主要作用 | 保证事务的原子性,用于事务回滚和 MVCC | 保证事务的持久性,用于崩溃恢复 | 用于数据恢复和主从复制 |
| 记录内容 | 数据的逻辑变化,记录的是"修改前的数据"(前镜像),如执行 INSERT 时记录对应的 DELETE 信息 | 数据的物理变化,记录的是"哪个数据页的哪个位置修改成了什么值" | SQL 语句的逻辑变化(或行变更),如执行一条 UPDATE 语句,记录对应的修改逻辑 |
| 记录时机 | 事务执行过程中,在修改数据之前生成 | 事务执行过程中不断写入,事务提交时必须写入磁盘 | 事务提交时,将事务中的所有修改一次性写入 |
| 文件数量 | 通常是单个或多个回滚段文件 | 固定数量的文件(循环写入,可配置) | 多个文件(可追加,可滚动) |
| 生命周期 | 事务结束后并不会立即删除,用于 MVCC 的历史版本读取,当没有事务需要访问旧版本数据时才会被清理 | 循环写入,旧日志会被覆盖 | 日志文件保留直到手动删除或达到过期时间 |
简单来说:
- undo log:负责撤销,记录"改之前的样子"
- redo log:负责重做,记录"改之后的样子"
- binlog:负责备份和同步,记录"怎么改的"