MySQL 中 redo log、undo log 以及 bin log 的区别
在 MySQL 数据库的底层架构中,有三个至关重要的日志子系统:redo log(重做日志)、undo log(回滚日志)和 bin log(二进制日志)。它们如同数据库的 "免疫系统",分别承担着事务持久化、数据回滚、增量备份与主从复制的核心功能。本文将从存储引擎底层实现出发,结合具体原理图,深入解析三者的技术原理与核心差异。
一、redo log:InnoDB 的事务持久化基石
1. 物理日志的本质
redo log 是 InnoDB 存储引擎独有的物理日志,记录的是数据页的物理修改操作,例如 "将数据页 0x123 的第 100 字节修改为 0x45"。其核心作用是实现 WAL(Write-Ahead Logging)机制,通过先写日志再写数据的方式,保证事务的持久性(ACID 中的 D)。
2. MySQL 中两个重点概念
- 缓冲池(buffer pool):主内存中的一个区域,里面可以缓存磁盘上经常操作的真实数据,在执行增删改查操作时,先操作缓冲池中的数据(若缓冲池没有数据,则从磁盘加载并缓存),以一定频率刷新到磁盘,从而减少磁盘 IO,加快处理速度
- 数据页(page):是 InnoDB 存储引擎磁盘管理的最小单元,每个页的大小默认为 16KB。页中存储的是行数据
示意图如下:图中 xxx.ibd 是InooDB 文件
为什么要区分内存结构和磁盘结构呢?当然是因为操作缓存比操作磁盘快啊!!!
当SQL 服务器正常时,就按上述二者的作用正常操作,当读取数据、修改数据、预读机制等情况,说明缓冲池在多种情况下都会和数据页交互;如果此时SQL 服务器突然宕机了,那么缓冲池中的数据就无法同步到 MySQL 中,此时就发生了数据丢失。
为了应对这种情况,提出了 redo log 日志。
redo log(重做日志),记录的是事务提交时数据页的物理修改,是用来实现事务的持久性。
该日志文件由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redo log file),前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改信息都存到该日志文件中,用于在刷新脏页到磁盘,发生错误时,进行数据恢复使用。
当然这里还可以有其他做法,比如同步,每存在一个 增删改 操作,我就去同步一次,每次同步都加一个返回,那也可以保证数据不丢失,即使丢失也能知道;但是这样会导致效率非常底,我缓冲池就是为了加快效率而存在的。
如下图所示:
当出现服务器宕机时,就会触发redo log。
3. 日志结构与写入流程
- 内存结构:redo log buffer 是 InnoDB 的内存缓冲区,采用环形数组结构,默认大小 16MB
- 刷盘策略:通过 innodb_flush_log_at_trx_commit 控制
-
- 0:每秒刷盘一次(性能最佳,可能丢失 1 秒数据)
-
- 1(默认):每次提交必刷盘(强一致性)
-
- 2:提交时写入 OS 缓存,由系统决定何时刷盘
从redo log 缓冲池中写入数据到磁盘结构是顺序的,相比于逆序顺序的效率要更高。
主要原因有:
磁盘读写特性
磁盘由多个盘片组成,盘片上的数据存储在磁道和扇区中 。进行随机读写时,磁盘的机械臂需要频繁移动来定位不同磁道和扇区,会产生寻道时间和旋转延迟;而顺序读写时,机械臂可以按顺序依次读取或写入相邻磁道扇区,无需频繁移动,能大幅减少寻道时间和旋转延迟,显著提升读写效率。redo log 顺序写入,符合磁盘高效读写特性。
性能优化层面
- 减少 I/O 次数:数据库运行中会产生大量 redo log 记录,若逆序写入,意味着频繁随机定位磁盘位置,每次定位都需额外 I/O 操作。顺序写入可将多个小的写操作合并成一个大的连续写操作,降低 I/O 操作次数,提升整体性能。比如事务执行过程中多条 redo log 记录,可批量顺序写入磁盘,而非分散多次随机写入。
- 降低系统开销:顺序写入方式简单直接,相比逆序写入复杂的定位和组织方式,对系统资源(如 CPU 计算资源、内存管理资源等)消耗更少,能让系统将更多资源用于处理核心业务逻辑。
数据恢复角度
- 方便日志解析:redo log 用于事务持久性和故障恢复,按顺序记录事务操作。顺序写入使日志记录按事务发生顺序排列,恢复时可按顺序逐一执行 redo 操作,还原数据到正确状态。若逆序,恢复时需重新梳理操作顺序,增加恢复难度和复杂性,可能导致恢复错误。
- 保证数据一致性:在崩溃恢复等场景下,顺序写入的 redo log 能确保先发生事务的修改先应用,后发生事务的修改后应用,避免数据混乱,保障数据一致性。若逆序,可能出现后发生事务先恢复、先发生事务后恢复的情况,破坏数据一致性。
简单点说,就是日志文件中存储了大量的 insert delete 语句,而日志文件都是追加的形式,顺序存储效率要更高。
这种顺序的写入方式也叫做 WAL(Write-Ahead Logging)机制
当脏页数据可以正常的同步到磁盘文件的时候,这个redo log 就没啥用了。每隔一段时间就会把 redo log 日志进行清理,这个redo log 日志 是有两份的,彼此之间循环写,如下图所示:
3. Checkpoint 机制
为避免日志无限增长,InnoDB 通过 checkpoint 机制将已持久化的数据从 redo log 中移除:
- 模糊检查点:定期将 Buffer Pool 中脏页刷盘,并更新 redo log 的 LSN(日志序列号)
- 尖锐检查点:数据库异常重启时,通过 LSN 定位需要恢复的日志范围
二、undo log:事务回滚与 MVCC 的核心载体
1. 逻辑日志的特性
undo log 是 InnoDB 实现事务原子性(ACID 中的 A)和 MVCC(多版本并发控制)的关键,记录的是数据修改前的反向操作,例如 "将数据页 0x123 的第 100 字节从 0x45 恢复为 0x36"。
2. MVCC
MVCC 即 Multi - Version Concurrency Control,多版本并发控制 ,是一种在数据库管理系统中广泛应用的并发控制方法,用于实现对数据库的并发访问 ,也可在编程语言中实现事务内存。主要依赖于数据库记录中的 隐式字段、undo log 日志、readView
MVCC 最主要的功能是确定在并发的环境下,可以确定用户到底要使用的是哪个版本。
隐藏字段
隐藏字段 | 含义 |
---|---|
DB_TRX_ID | Database Transaction ID 事务 ID,最近修改事务 ID,记录插入这条记录或最后一次修改该记录的事务 ID。 |
DB_ROLL_PTR | Database Roll Point 回滚指针,指向这条记录的上一个版本,用于配合 undo log,指向上一个版本。 |
DB_ROW_ID | Database Row ID 隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段。 |
undo log
- 回滚日志,在 insert、update、delete 的时候产生的便于数据回滚的日志。
- 当 insert 的时候,产生的 undo log 日志只在回滚时需要,在事务提交后,可被立即删除。
- 而 update、delete 的时候,产生的 undo log 日志不仅在回滚时需要,mvcc 版本访问也需要,不会立即被删除。
版本链 和 readView
undo log 通过构建数据版本链实现历史数据访问:
- 版本链:每个数据行通过 trx_id 和 roll_ptr 字段链接多个历史版本
类似于上图,将多个版本形成一个链表。
- Read View:事务启动时生成的快照,包含当前活跃事务 ID 列表,通过比较 trx_id 判断版本可见性
ReadView(读视图)是快照读 SQL 执行时 MVCC 提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id。
readView 又分为当前读和快照读
- 当前读:
- 读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如:select ... lock in share mode (共享锁),select ... for update、update、insert、delete (排他锁) 都是一种当前读。
- 当有事务在读取的时候,又有一个事务来进行写操作,并且提交了,但是该事务还是能够读取到最新数据,即使是发生阻塞也要读取到最新数据。
- 快照读:简单的 select(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读;收到不同的隔离级别影响,不同的隔离级别,会有不同的结果
- Read Committed:每次 select,都生成一个快照读。
- Repeatable Read:开启事务后第一个 select 语句才是快照读的地方。(在同一个事务下无论读取多少次,读到的结果都一样)
不同的隔离级别,生成 ReadView 的时机不同:
- READ COMMITTED:在事务中每一次执行快照读时生成 ReadView。
- REPEATABLE READ:仅在事务中第一次执行快照读时生成 ReadView,后续复用该 ReadView。
ReadView 中包含的四个核心字段
字段 | 含义 |
---|---|
m_ids | 当前活跃的事务 ID 集合(Active Transaction IDs),记录了在创建 ReadView 时,数据库中所有尚未提交的事务 ID。 |
min_trx_id | 最小活跃事务 ID(Minimum Transaction ID),是 m_ids 集合中最小的事务 ID,用于判断事务的可见性。 |
max_trx_id | 预分配事务 ID(Maximum Transaction ID),值为当前最大事务 ID + 1(因为事务 ID 是自增的),用于界定事务 ID 的范围。 |
creator_trx_id | ReadView 创建者的事务 ID(Creator Transaction ID),标识创建该 ReadView 的事务。 |
MVCC 小结
MySQL 中的多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突
- 隐藏字段:
① trx_id (事务 id),记录每一次操作的事务 id,是自增的
② roll_pointer (回滚指针),指向上一个版本的事务版本记录地址 - undo log:
① 回滚日志,存储老版本数据
② 版本链:多个事务并行操作某一行记录,记录不同事务修改数据的版本,通过 roll_pointer 指针形成一个链表 - readView 解决的是一个事务查询选择版本的问题
- 根据 readView 的匹配规则和当前的一些事务 id 判断该访问那个版本的数据
- 不同的隔离级别快照读是不一样的,最终的访问的结果不一样
- RC:每一次执行快照读时生成 ReadView
- RR:仅在事务中第一次执行快照读时生成 ReadView,后续复用
小结:
回滚日志,用于记录数据被修改前的信息,作用包含两个:提供回滚 和 MVCC (多版本并发控制)。undo log 和 redo log 记录物理日志不一样,它是逻辑日志。
- 可以认为当 delete 一条记录时,undo log 中会记录一条对应的 insert 记录,反之亦然,
- 当 update 一条记录时,它记录一条对应相反的 update 记录。当执行 rollback 时,就可以从 undo log 中的逻辑记录读取到相应的内容并进行回滚。
undo log 可以实现事务的一致性和原子性
三、bin log:MySQL 服务器层的增量记录
1. 逻辑日志的定位
bin log 是 MySQL 服务器层(而非存储引擎)的日志,记录所有修改数据的 SQL 语句或数据行变更,用于实现:
- 基于时间点的恢复(Point-in-Time Recovery)
- 主从复制(Master-Slave Replication)
- 审计追踪(需配合其他工具)
这里就只简单说说主从复制的原理。
- bin log 存在于主节点,功能是 记录所有修改数据的 SQL 语句或数据行变更。
- 而在从节点存在一个 IO 线程,这个线程只干一件事,就是去读 bin log 中的日志,并记录到 relay log (中继日志)中。
- 另外从节点还有一个 SQL 线程,这个线程用于将中继日志记录到的语句,写入从节点中,进而实现数据同步。
2. 三种日志格式
格式 | 记录内容 | 优点 | 缺点 |
Statement | SQL 语句文本 | 日志体积小 | 可能导致主从数据不一致(如 UUID ()) |
Row | 数据行的前后镜像 | 绝对一致性 | 日志体积大(可能是 SQL 的 10 倍) |
Mixed | 自动切换上述两种格式 | 平衡一致性与体积 | 存在格式切换的不确定性 |
3. 写入与复制流程
- 两阶段提交:bin log 与 redo log 通过 XID(事务 ID)关联,保证提交的原子性
- sync_binlog:控制 bin log 刷盘策略(0 = 由 OS 控制,1 = 每次提交刷盘,n = 每 n 次提交刷盘)
四、核心区别对比表
特征 | redo log | undo log | bin log |
所属模块 | InnoDB 存储引擎 | InnoDB 存储引擎 | MySQL 服务器层 |
日志类型 | 物理日志(数据页修改) | 逻辑日志(反向操作) | 逻辑日志(SQL / 行变更) |
作用 | 事务持久化 | 事务回滚 / MVCC | 增量备份 / 主从复制 |
写入时机 | 事务修改时异步生成 | 事务修改前生成 | 事务提交时同步写入 |
存储位置 | ib_logfile * 文件 | 回滚段(共享表空间或独立表空间) | 主机文件系统(路径由 log_bin 指定) |
日志格式 | 专有格式(ibd) | 专有格式 | Statement/Row/Mixed |
生命周期 | 由 Checkpoint 控制 | 由 Purge 线程清理 | 由 expire_logs_days 控制(默认 0 不自动删除) |
五、典型应用场景
1. 事务故障恢复
- 崩溃恢复:InnoDB 通过 redo log 重做未提交的事务,通过 undo log 回滚已提交但未持久化的事务
- 手动回滚:ROLLBACK 语句通过 undo log 执行反向操作,快速撤销事务影响
2. 高可用性架构
- 主从复制:bin log 作为主库与从库的数据桥梁,实现异步复制(默认)、半同步复制(增强一致性)
- 数据备份:通过 mysqldump 结合 bin log,实现基于时间点的精确恢复
3. 并发控制
- MVCC 读:通过 undo log 的版本链,实现读不阻塞写,提升高并发场景下的性能
- 锁优化:减少行锁使用,降低锁竞争导致的性能瓶颈
六、最佳实践建议
- redo log 配置:根据业务 IO 特性调整日志文件大小(innodb_log_file_size),建议设置为可用内存的 25%-50%
- undo log 管理:定期监控回滚段空间使用(SHOW ENGINE INNODB STATUS),避免长事务导致的空间膨胀
- bin log 安全:生产环境建议使用 Row 格式(binlog_format=ROW),并开启 bin log 加密(binlog_encryption=ON)
- 性能平衡:通过 sync_binlog=1+innodb_flush_log_at_trx_commit=1 实现强一致性,或根据容忍度调整参数提升写入性能
总结
三大日志系统犹如数据库的 "三驾马车":redo log 保障数据不丢失,undo log 维护事务原子性与数据多版本,bin log 构建增量复制与恢复体系。理解它们的底层原理,不仅能帮助我们优化数据库性能,更能在故障处理、架构设计时做出正确决策。当遇到事务异常、主从延迟或空间占用过高等问题时,从这三大日志入手分析,往往能快速定位到核心原因。
(注:本文原理图可根据实际需求绘制,建议包含 redo log 写入流程、undo log 版本链结构、bin log 主从复制架构等示意图,帮助读者更直观理解日志机制。)