mysql三大日志 redolog undolog binlog

核心概念:

特性 redo log (重做日志) undo log (回滚日志) binlog (二进制日志)
所属层次 InnoDB 存储引擎层 InnoDB 存储引擎层 MySQL Server 层
主要目的 崩溃恢复,保证持久性 (D) 事务回滚,MVCC,保证原子性 (A) 数据复制,时间点恢复
记录内容 物理日志:页的物理修改 逻辑日志:更新前的行数据/旧版本 逻辑日志:SQL 语句/行变化逻辑
生命周期 事务提交后很快可覆盖 事务提交后可能保留较长时间 (MVCC) 长期保留 (可配置过期)
写入方式 循环写 (固定大小文件) 离散存储 (回滚段) 追加写 (不断生成新文件)
持久化点 事务提交时可强制刷盘 随 redo log 持久化 事务提交时可强制刷盘
关键文件 ib_logfile0, ib_logfile1 ibdata1 (系统表空间) / undo 表空间 mysql-bin.000001, ...
是否必需 InnoDB 必需 InnoDB 必需 可选 (但强烈推荐开启)

1. redo log (重做日志)

  • 作用:

    • 崩溃恢复 (Crash Recovery): 这是 redo log 最核心的作用。确保已提交事务对数据的修改在数据库崩溃后不会丢失(保证持久性 Durability)。
    • Write-Ahead Logging (WAL) 机制的核心: InnoDB 修改数据页时,并不是直接写回磁盘数据文件,而是先在内存中修改(脏页),并将这次修改对应的 物理日志 顺序、高效地写入 redo log buffer,然后在适当时机(如事务提交时)刷盘到 redo log 文件。之后在后台再将内存中的脏页刷新到磁盘数据文件。这种方式大大减少了随机磁盘 I/O,提高了性能。
    • 支持组提交 (Group Commit): 多个事务的 redo log 可以合并成一次 I/O 刷盘,进一步提升提交效率。
  • 数据结构:

    • 物理日志: 记录的是对某个数据页 (Page) 的某个偏移量 (Offset) 处做了哪些修改。它不是逻辑的 SQL 语句,而是非常底层的物理操作描述(如:将 Page X 偏移量 Y 处的 Z 字节更新为值 W)。
    • 格式:
      • Log Sequence Number (LSN): 一个单调递增的日志序列号,唯一标识 redo log 中的每个记录。它是崩溃恢复的核心依据。
      • Log Type: 标识日志记录的类型(如 MLOG_1BYTE, MLOG_2BYTES, MLOG_4BYTES, MLOG_8BYTES, MLOG_WRITE_STRING, MLOG_COMP_REC_INSERT, MLOG_COMP_REC_DELETE 等,对应不同的修改操作)。
      • Space ID: 表空间 ID。
      • Page Number: 被修改的页号。
      • Data: 修改的具体内容(取决于 Log Type,可能是修改的偏移量、长度、新旧数据片段等)。
    • 存储:
      • 通常由两个物理文件组成:ib_logfile0ib_logfile1 (默认名称和数量可配置)。
      • 循环写入 (Circular Buffer): redo log 文件的大小是固定的。当日志写满时,会回到开头覆盖写入。覆盖的前提是这部分日志对应的脏页已经被刷新到磁盘数据文件中(由 Checkpoint 机制保证)。
      • Checkpoint: 一个关键的 LSN 位置,表示在这个 LSN 之前的 redo log 所对应的脏页都已经被刷新到磁盘数据文件了。崩溃恢复时,只需要从 Checkpoint 对应的 LSN 开始重做后续的 redo log 即可。
  • 关键流程 (事务提交时):

    1. 事务执行修改操作。
    2. 生成对应的 redo log 记录,写入内存中的 redo log buffer
    3. 事务提交时(或根据 innodb_flush_log_at_trx_commit 设置),redo log buffer 的内容被写入操作系统的 Page Cache(write 系统调用)。
    4. 根据 innodb_flush_log_at_trx_commit 设置,可能强制调用 fsync() 将 Page Cache 中的 redo log 刷到磁盘物理文件(保证持久性)。
      • =0: 每秒由后台线程刷盘一次(最快,可能丢最多1秒数据)。
      • =1: 每次事务提交都刷盘(最安全,性能略差)。
      • =2: 每次事务提交只写入 Page Cache(操作系统崩溃可能丢数据,服务器断电不会丢)。

2. undo log (回滚日志)

  • 作用:

    • 事务回滚 (Rollback): 保证事务的原子性 (Atomicity)。如果事务执行失败或显式 ROLLBACK,undo log 记录了如何撤销该事务对数据库所做的修改,将数据恢复到事务开始前的状态。
    • 多版本并发控制 (MVCC): 这是 undo log 在现代数据库中的另一个极其重要的作用。当某个读操作需要访问一个正在被修改或已被修改但未提交的行时,或者需要读取一个历史版本的行时,undo log 中存储的旧版本数据就被用来构建该行的“快照”或“历史版本”,从而实现非锁定读取(一致性读),保证了隔离性 (Isolation),特别是 REPEATABLE READ 隔离级别。
    • 崩溃恢复辅助: 在崩溃恢复的回滚阶段 (Rollback Phase),undo log 用于回滚那些未提交的事务。
  • 数据结构:

    • 逻辑日志: 记录的是修改操作的逆操作。它与 redo log 的物理记录不同。
      • 对于 INSERT: undo log 记录该新行的主键值。回滚时据此删除该行。
      • 对于 DELETE: undo log 记录该行的完整内容(包括所有列)。回滚时据此重新插入该行。
      • 对于 UPDATE: undo log 记录被修改列更新前的值(或者整行旧值,取决于配置)。回滚时据此将列的值恢复回去。
    • 存储:
      • 存储在特殊的 回滚段 (Rollback Segments) 中。每个回滚段包含多个 undo slot
      • 回滚段本身存储在系统表空间 (ibdata1) 或独立的 undo 表空间 (从 MySQL 5.6 开始支持,推荐使用)。
      • 离散存储: 不像 redo log 是顺序循环写,undo log 的写入相对离散。
      • 链式结构: 同一个行记录的不同历史版本通过 DB_ROLL_PTR (rollback pointer) 指针形成一个链表(版本链),链表头指向最新的版本。MVCC 就是通过遍历这个版本链来找到合适的历史版本。
    • 生命周期:
      • 当事务提交后,其产生的 undo log 不会立即删除。
      • 这些 undo log 会被放入一个“历史链表”中,只有当系统中没有任何活跃的读事务还需要这个旧版本来构建一致性视图 (Read View) 时,对应的 undo log 空间才会被标记为可重用或被最终清理 (purge 线程负责)。这是支持长事务 MVCC 的关键。

3. binlog (二进制日志)

  • 作用:

    • 数据复制 (Replication): 这是 binlog 最主要的作用。主库 (Master) 将 binlog 发送给从库 (Slave),从库通过重放 binlog 中的事件来保持与主库的数据一致,实现主从同步、读写分离、高可用等。
    • 时间点恢复 (Point-in-Time Recovery, PITR): 结合全量备份和 binlog,可以将数据库恢复到备份后的任意时间点(需要 binlog 保存完整)。
    • 审计 (Auditing): 记录所有更改数据库数据的操作(DML、DDL、DCL),可用于安全审计。
  • 数据结构:

    • 逻辑日志: 记录的是引起数据变更的逻辑操作。主要有三种格式:
      • Statement (SBR - Statement-Based Replication): 记录原始的 SQL 语句本身(如 UPDATE users SET name='foo' WHERE id=1;)。优点:日志量小。缺点:某些非确定性语句(如 NOW(), RAND(), UUID())或依赖特定上下文(如存储过程、触发器)可能导致主从不一致。不推荐作为默认格式。
      • Row (RBR - Row-Based Replication): 记录每一行数据修改前后的内容(或修改后的内容)。优点:最安全,主从一致性最高。缺点:日志量可能非常大(尤其是批量更新)。推荐作为默认格式
      • Mixed (MBR - Mixed-Based Replication): MySQL 自动选择使用 SBR 还是 RBR。通常安全的语句用 SBR,可能不安全的(如包含非确定性函数)自动切换到 RBR。是一种折中方案。
    • 存储:
      • 由一系列按序号命名的文件组成:mysql-bin.000001, mysql-bin.000002, ...。
      • 追加写入 (Append-Only): binlog 文件不会循环覆盖。当文件达到指定大小 (max_binlog_size) 后,会自动切换到下一个文件继续写。
      • 索引文件: mysql-bin.index 文件记录当前所有有效的 binlog 文件名。
    • 写入时机:
      • Binlog 的写入发生在事务执行过程中(语句执行后),但提交 (fsync) 是在事务最终提交阶段完成的(在 InnoDB 引擎将事务标记为提交之后,但在返回成功给客户端之前)。这是二阶段提交的关键点之一。
      • 通过 sync_binlog 参数控制刷盘策略:
        • =0: 依赖操作系统刷盘(性能最好,风险最高)。
        • =1: 每次事务提交都刷盘(最安全,性能有影响)。
        • =N (N>1): 每 N 个事务提交后刷盘一次(折中)。

面试高频问题及答案

  1. 为什么需要 redo log?直接修改数据文件不行吗?

    • :直接修改数据文件效率太低(随机 I/O)。redo log 采用 WAL 机制,先顺序写日志(redo log 是顺序 I/O),再异步刷脏页到磁盘数据文件(可以合并 I/O),极大地提高了事务提交的速度和数据库的整体吞吐量。同时,它保证了已提交事务的持久性(崩溃恢复)。
  2. redo log 和 binlog 有什么区别?

    • :核心区别如下表:
      方面 redo log (InnoDB) binlog (MySQL Server)
      所属层 存储引擎层 Server 层
      目的 崩溃恢复 (持久性) 复制、时间点恢复、审计
      内容 物理日志 (页的修改) 逻辑日志 (SQL语句/行变化)
      写入 循环写 追加写
      文件 ib_logfile* mysql-bin.*
      必需性 InnoDB 必需 可选 (但强烈建议)
      格式 固定格式 Statement/Row/Mixed
  3. 什么是二阶段提交 (2PC)?为什么需要它?

    • :二阶段提交是 MySQL 内部用来协调 InnoDB 的 redo log 和 Server 层的 binlog,保证两者在事务提交时逻辑一致的机制。主要为了在崩溃恢复时,确保数据主库和从库(通过 binlog)的数据一致性。
    • 流程
      1. Prepare 阶段: InnoDB 将事务状态标记为 PREPARE,并将该事务的 redo log 刷盘 (fsync)。
      2. Write & fsync binlog: MySQL Server 将 binlog 写入文件并刷盘 (fsync)。
      3. Commit 阶段: InnoDB 将事务状态标记为 COMMIT (写入 redo log,但此时可以不强制刷盘,依赖后台)。
    • 为什么需要:如果只有一阶段提交,在崩溃恢复时可能出现:
      • redo log 有 commit,binlog 没有:主库数据比 binlog 多,从库会丢失数据。
      • binlog 有,redo log 没有 commit:主库数据比 binlog 少,从库会有多余数据。
        2PC 通过 redo log 的 prepare 状态和 binlog 的完整性作为判断依据,保证了两者的原子性。
  4. 崩溃恢复时,如何利用 redo log 和 binlog?

      1. 重做阶段 (Redo Phase): 从 Checkpoint 开始扫描 redo log,重放所有 redo log(包括状态为 prepare 和 commit 的),将数据库恢复到崩溃前的物理状态(包括未提交的事务修改)。
      2. 回滚阶段 (Undo Phase):
        • 扫描 redo log 找到所有处于 PREPARE 状态的事务。
        • 检查这些事务对应的 binlog:
          • 如果该事务的 binlog 存在且完整(有 COMMIT 标记),说明该事务在主库已经成功提交(binlog 已 fsync),则在 InnoDB 层将其提交(写入 commit 标记)。
          • 如果该事务的 binlog 不存在或不完整,说明该事务在主库没有最终提交成功(可能在 binlog fsync 前崩溃),则在 InnoDB 层利用 undo log 将其回滚
            这个过程保证了主库数据与 binlog 记录的事务提交状态最终一致。
  5. undo log 是如何支持 MVCC 的?

      • 当一行数据被修改时,InnoDB 会将修改前的数据(旧版本)拷贝到 undo log 中。
      • 该行的隐藏字段 DB_TRX_ID 记录最后修改它的事务 ID,DB_ROLL_PTR 指向 undo log 中该行的上一个版本记录。
      • 这样就形成了一个基于 undo log 的行记录版本链
      • 当一个读事务开始时,它会生成一个 Read View(快照),记录当前活跃(未提交)的事务 ID 列表和一些关键信息(如最小活跃事务 ID up_limit_id,下一个将要分配的事务 ID low_limit_id)。
      • 当该事务访问某一行时,它会沿着版本链查找:
        • 如果某版本的 DB_TRX_ID < up_limit_id,说明该版本在 Read View 生成前已提交,可见
        • 如果某版本的 DB_TRX_ID >= low_limit_id,说明该版本在 Read View 生成后才修改,不可见,继续找上一个版本。
        • 如果 up_limit_id <= DB_TRX_ID < low_limit_id,且 DB_TRX_ID 在活跃事务列表中,说明修改该版本的事务在 Read View 生成时还未提交,不可见;否则可见
      • 通过这种方式,读事务可以访问到它启动时已提交的数据版本,实现了非锁定读取(快照读)和 REPEATABLE READ 隔离级别。
  6. binlog 的三种格式 (Statement/Row/Mixed) 有什么区别?推荐哪种?为什么?

      • Statement (SBR):
        • 优点:日志量小,节省磁盘和网络 I/O。
        • 缺点:非确定性函数 (NOW, RAND, UUID, USER)、存储过程/触发器、AUTO_INCREMENT 列更新等可能导致主从不一致。调试相对困难。
      • Row (RBR):
        • 优点:最安全,主从数据一致性最高。记录了每一行的实际变化,易于理解数据变更。
        • 缺点:日志量大(尤其批量更新、大字段更新)。可能增加磁盘、网络 I/O 和复制延迟。
      • Mixed (MBR):
        • 优点:MySQL 自动选择,在安全性和日志量之间折中。
        • 缺点:仍有小概率(虽然很低)在自动切换时可能出现预期外的不一致(极罕见)。
    • 推荐Row-Based Replication (RBR)。在现代硬件条件下,其带来的安全性优势远大于日志量增加的代价。MySQL 5.7.7 及以后版本默认也是 RBR。
  7. 参数 innodb_flush_log_at_trx_commitsync_binlog 的作用?如何配置保证数据安全?

      • innodb_flush_log_at_trx_commit (控制 redo log 刷盘):
        • =0: 每秒刷盘。性能最好,崩溃可能丢最多 1 秒数据。不安全。
        • =1: 每次事务提交都刷盘。最安全,保证已提交事务不丢。性能较差。
        • =2: 每次提交写 OS Page Cache,依赖 OS 每秒刷盘。服务器断电丢数据,OS 崩溃不丢。性能较好。
      • sync_binlog (控制 binlog 刷盘):
        • =0: 依赖 OS 刷盘。不安全。
        • =1: 每次事务提交都刷盘。最安全,保证 binlog 不丢。性能较差。
        • =N (N>1): 每 N 个事务提交刷盘一次。折中,崩溃可能丢最后 N-1 个事务的 binlog。
    • 保证最高安全配置
      • innodb_flush_log_at_trx_commit = 1
      • sync_binlog = 1
        此配置下,每个事务提交都需要等待两次 fsync (一次 redo, 一次 binlog),性能最低,但数据最安全。需要根据业务对性能和可靠性的要求权衡。
  8. 事务提交后,undo log 会立刻删除吗?

    • 不会立刻删除。事务提交后,其产生的 undo log 会被放入一个“历史链表”中。这些 undo log 必须保留,直到系统中没有任何活跃的读事务还需要它们来构建一致性读视图 (Read View) 为止。后台的 purge 线程负责在确认某个 undo log 不再被任何 MVCC 读视图需要后,才清理其空间。长事务会阻碍 undo log 的清理,可能导致 undo 表空间膨胀。
  9. 描述一个 UPDATE 语句的执行过程中,这三种日志是如何参与的?

      1. 开始事务 (隐式或显式)。
      2. 执行 UPDATE:
        • 在 Buffer Pool 中找到目标数据页(不在则从磁盘加载)。
        • 修改内存中的数据行(产生脏页)。
        • 写 undo log:将更新前的行数据(或旧值)写入 undo log buffer/page。设置行记录的 DB_ROLL_PTR 指向这个 undo log 记录。
        • 写 redo log:将对数据页的物理修改写入 redo log buffer。记录状态为 prepare (此时未提交)。
      3. 事务提交:
        • Prepare Phase: 将包含该事务 prepare 状态的 redo log 刷盘 (fsync)。
        • Write & fsync binlog: 将该 UPDATE 操作对应的 binlog event 写入 binlog 文件并刷盘 (fsync)。
        • Commit Phase: 在 redo log 中写入该事务的 commit 记录(此时刷盘可延迟)。
      4. 返回成功给客户端
      5. 后台:InnoDB 后台线程负责将脏页刷新到磁盘数据文件。Purge 线程在合适时机清理不再需要的旧版本 undo log。

理解 redo log, undo log 和 binlog 的作用、结构以及它们之间的协作机制,是深入掌握 MySQL 事务、并发控制、崩溃恢复和高可用架构的基础。面试中围绕这些点的深入探讨非常常见。

posted @ 2025-08-29 10:59  adragon  阅读(2)  评论(0)    收藏  举报