MySQL 的 Redo Log(重做日志)是 InnoDB 存储引擎的核心组件之一,是保证数据库持久性(Durability) 和崩溃恢复(Crash Recovery) 的关键机制。
1. 什么是 Redo Log?它的核心作用是什么?
核心作用: 保证事务的持久性(Durability)。
这意味着,只要一个事务成功提交(COMMIT),那么它对数据库所做的修改就绝对不会丢失,即使随后发生数据库宕机、断电等故障,在重启后也能通过 Redo Log 恢复这些已提交的修改。
简单比喻:
想象一下一家生意火爆的餐馆。
磁盘上的数据文件(.ibd):就像后厨的正式账本,记录着所有订单的最终状态。但每做一笔都直接记上去,效率太低。
Buffer Pool(内存缓冲池):就像服务员手里的点餐小票,记录了最新的订单。读写速度快,但一撕就丢(掉电丢失)。
Redo Log:就像服务员飞速记在黑板上的订单。客人下单(事务提交)后,服务员会立刻先把订单写到黑板上,然后再慢慢交给后厨处理。即使后厨还没来得及把菜做完(数据没刷盘)或者服务员手里的小票丢了(内存丢失),只要黑板上的字还在,就能知道客人点了什么,确保订单不会丢失。
这个“先写黑板,再慢慢处理”的过程,就是著名的 Write-Ahead Logging (WAL) 技术,而 Redo Log 就是实现 WAL 的载体。
2. 为什么需要 Redo Log?—— 解决性能问题
如果没有 Redo Log,为了保证持久性,InnoDB 必须在每次事务提交时,都将该事务修改的所有数据页随机地、同步地刷新到磁盘上。
随机 I/O:数据页在磁盘上分布是零散的,写入是随机写,速度很慢。
同步 I/O:提交操作必须等待所有慢速的磁盘 I/O 完成,用户线程会被阻塞。
这会产生巨大的性能瓶颈,因为磁盘的随机 I/O 速度远低于内存操作和顺序 I/O。
Redo Log 的解决方案:
顺序 I/O:Redo Log 的记录是追加写入的,是顺序写,速度极快(甚至可以用专门的 SSD 优化)。
组合写入:多个事务的修改可以合并在一起,一次性写入日志文件,大大减少了磁盘 I/O 次数。
异步刷盘:事务提交时,只需要保证 Redo Log 成功写入(顺序写,很快),就可以返回成功。内存中的数据页(脏页)则由后台线程异步地、批量地刷新到磁盘(随机写),这个操作不会阻塞用户请求。
所以,Redo Log 通过将随机写转换为顺序写,极大地提升了数据库的写入性能,同时保证了事务的持久性。
3. Redo Log 的组成与工作流程
物理结构
Redo Log 在物理上由两个文件组成,通常命名为 ib_logfile0
和 ib_logfile1
。它们被设计成循环写入的固定大小文件。
innodb_log_file_size
: 每个 Redo Log 文件的大小。innodb_log_files_in_group
: Redo Log 文件的数量,默认为 2。总日志大小 =
innodb_log_file_size
*innodb_log_files_in_group
。
逻辑结构
逻辑上,Redo Log 是一个连续的顺序写入空间。为了管理循环写入,它维护了几个关键指针:
write pos
(写入位置):当前记录写入的位置,随着写入不断后移。checkpoint
(检查点):表示已经刷新到磁盘数据文件的位置。它之前的数据已经被持久化,对应的日志空间可以被覆盖。
write pos
和 checkpoint
之间的空间是空闲的可写入部分。当 write pos
追上 checkpoint
(即日志文件写满)时,数据库必须停下来,先推进 checkpoint
(强制刷脏页),释放出可重用的日志空间,然后才能继续处理新的更新操作。因此,设置过小的 Redo Log 文件会导致频繁的“卡顿”。
工作流程(以 UPDATE 为例)
数据载入:要修改的数据页如果不在 Buffer Pool 中,则从磁盘加载到 Buffer Pool。
修改内存:在 Buffer Pool 中修改数据页,使其变成“脏页”。
写入 Redo Log Buffer:生成一条 Redo Log 记录,描述这个“物理页面”上的修改(如:表空间ID、页号、偏移量、修改后的值等)。这条记录首先被写入到内存中的 Redo Log Buffer。
事务提交:当用户执行
COMMIT
时,根据innodb_flush_log_at_trx_commit
的设置,InnoDB 会采取不同的行为将 Redo Log Buffer 中的内容刷新到磁盘的 Redo Log 文件。这是保证持久性的关键步骤。刷盘通知:事务提交后,即可返回客户端成功。后台的 I/O 线程会在未来某个时间点将 Buffer Pool 中的“脏页”刷新到磁盘的数据文件中。
推进 Checkpoint:当脏页被成功刷盘后,对应的 Redo Log 记录就失去了作用(已经持久化了)。Checkpoint 位置就可以向前推进,这部分日志空间可以被后续的写入覆盖。
4. 关键配置参数:innodb_flush_log_at_trx_commit
这个参数控制了事务提交时,刷新 Redo Log 到磁盘的策略,是在性能和持久性保证之间进行权衡的关键。
= 1 (默认值)
行为:每次事务提交时,都会将 Redo Log Buffer 的内容同步写入并刷新(fsync) 到磁盘。
保证:最严格的持久性保证。即使宕机,也绝不会丢失任何已提交的事务。
缺点:因为每次提交都要发生一次磁盘 I/O(虽然是顺序的),性能是最差的。
= 2
行为:每次事务提交时,仅将 Redo Log Buffer 的内容写入到操作系统的页面缓存(Page Cache),但不执行
fsync
刷盘操作。保证:只有在操作系统不宕机(比如MySQL进程崩溃,但服务器没重启)的情况下,才能保证不丢数据。如果服务器断电,而操作系统缓存中的数据还没来得及刷盘,那么这部分数据就会丢失。
性能:比
=1
好,因为写入 Page Cache 非常快。
= 0
行为:每秒一次地将 Redo Log Buffer 的内容写入 Page Cache 并调用
fsync
刷到磁盘。事务提交时本身不会触发任何写入。保证:安全性最差。如果 MySQL 进程崩溃,最多会丢失 1 秒内提交的事务。如果服务器断电,最多可能丢失超过 1 秒的数据(因为还可能有一部分在 Buffer 里没写到 Page Cache)。
性能:最好。
如何选择?
要求绝对数据安全(如金融交易):必须使用
1
。可以容忍丢失极少量数据(如点赞、评论):可以设置为
2
,性能提升显著。对性能要求极高,且数据不重要:可考虑
0
(一般不推荐)。
5. Redo Log 与 Binlog 的区别
这是一个经典面试题。虽然都是日志,但两者截然不同:
特性 | Redo Log | Binlog (二进制日志) |
---|---|---|
归属 | InnoDB 引擎层独有的 | MySQL Server 层实现的,所有存储引擎都可以使用 |
类型 | 物理日志 记录的是“在某个数据页上做了什么修改” | 逻辑日志 记录的是语句的原始逻辑(如 UPDATE t SET f1=1 )或行变更前后的镜像 |
写入方式 | 循环写 文件固定大小,写满会覆盖 | 追加写 文件写完后会切换到下一个,不会覆盖旧日志 |
用途 | 崩溃恢复 (Crash Recovery) 保证事务的持久性 | 数据归档、主从复制 (Replication) Point-in-Time Recovery(按时间点恢复) |
在 MySQL 5.6 引入 两阶段提交(2PC) 之前,需要保证 Redo Log 和 Binlog 的一致性是一个复杂的问题。两阶段提交机制确保了即使发生崩溃,二者也能保持逻辑上的一致。
总结
是什么:Redo Log 是 InnoDB 的物理日志,采用循环写入方式。
为什么:为了将随机写转换为顺序写,提升写入性能,同时保证事务的持久性(WAL 技术的实现)。
怎么做:事务提交时,先强制写入 Redo Log(取决于
innodb_flush_log_at_trx_commit
设置),再异步刷脏页。关键点:通过
write pos
和checkpoint
管理循环写入,通过配置参数在性能和数据安全间做权衡。区别:与 Server 层的逻辑日志 Binlog 在归属、类型、用途上完全不同。
理解 Redo Log 是理解 InnoDB 如何协调高性能与数据安全性的基石。