三大日志

binlog

二进制日志

Server层的日志,逻辑日志,用来数据备份和主从复制

写入时机

在每完成一次更新操作之后,生成一条binlog

刷盘时机

什么时候刷盘由sync_binlog参数决定

  • 0:不去强制要求,由系统自行判断何时写入磁盘;
  • 1:每次事务提交后,都要将binlog写入磁盘;
  • N:每N个事务,才会将binlog写入磁盘。

方案

STATEMENT:记录数据变更的SQL语句本身

  • 好:日志文件体积小,一SQL记一次
  • 坏:可能存在数据不一致,对于动态函数---NOW()UUID()LIMIT等依赖上下文,会导致主从数据不一致

ROW:记录数据最终被修改成什么样

  • 好:数据一致性最高,绝对一致主从
  • 坏:日志文件体积大,更新多少行就记多少

    MIXED:折中判断

特点

追加写,写满一个文件,就创建一个新的文件继续写,不覆盖,存全量日志--->恢复,主从复制

注意的是只记录库、表结构改变和数据修改的日志

redolog

重做日志

存储引擎层面的,物理日志,用来保障事务的持久性和崩溃恢复

Mysql有Buffer Pool缓冲池,跟redis一样。如果在Buffer Pool中的页的修改还没持久化到磁盘中,这个时候宕机了,事务更改就消失了

引入WAL技术,就是先写到日志,再写到磁盘上

image-20251010110214968
image-20251010110214968

写入时机

  • 当执行一条更新语句(如 UPDATE)时,先在 Buffer Pool 中查找对应的数据页。如果不在,就从磁盘加载到 Buffer Pool。
  • 接着,修改 Buffer Pool 中的这个数据页。此时,这个被修改了但还没刷回磁盘的数据页,就是所谓的 “脏页”
  • 标记脏页的同时,InnoDB把这个页面做了什么物理修改记录记录到Redo Log Buffer中
  • 每一次数据变更都会产生对应的redo log
例如:在表空间xx文件、第xx页、偏移量xx的位置写入了xxx数据

刷盘时机

事务提交时,先把 Redo Log Buffer 中对应的所有日志记录,强制刷盘,写到redolog中, 此时,现在已经算是被持久化了。(由参数调控)

  1. trx_commit = 1:每次事务提交时,都必须立刻将 redo log 从内存刷入磁盘。(默认)
  2. trx_commit = 0:不保证事务提交时刷盘,而是大约每秒钟由后台线程将 redo log 从内存刷入磁盘一次。不安全
  3. trx_commit = 2:每次事务提交时,只把 redo log 从内存写入到操作系统的页缓存(Page Cache),不直接写入磁盘文件。然后大约每秒钟,操作系统会把它的缓存统一刷到磁盘。 折中

因为即使此时Buffer Pool 中的脏页还没有写回数据库,崩溃的时候,也可以依靠Redo log来进行重做,恢复到事务最后一次提交的状态

这就是WAL思想,先写日志,后写数据

两阶段提交

不一致问题

保障binlog和redolog的数据一致性,不一致的问题:

  1. 先写 Redo Log,后写 Binlog,但在写 Binlog 前宕机:

    • Redo Log 已经持久化,数据可以恢复。
    • Binlog 没有记录。
    • 后果: 主库数据是完整的,但从库(通过 Binlog 复制)会缺失这条数据,导致主从数据不一致。
  2. 先写 Binlog,后写 Redo Log,但在写 Redo Log 前宕机:

    • Binlog 已经记录,从库会复制这条数据。
    • Redo Log 没有完全持久化,崩溃恢复时主库会回滚这条数据(WAL 思想失败)。
    • 后果: 从库多出一条数据,主从数据不一致。

阶段一:Prepare 准备阶段

  1. 数据变更: 事务执行所有 SQL 语句,修改 Buffer Pool 中的数据页,并生成相应的 Redo Log 记录。
  2. Redo Log 刷盘:存储引擎将 Redo Log Buffer 中的数据刷入磁盘。
  3. 标记 XID: 在这条 Redo Log 记录上,会标记一个事务 ID (XID),并将其状态设置为 "Prepare"(准备状态)。
状态: Redo Log 已持久化到磁盘,但事务在 InnoDB 内部仍未最终提交。如果此时宕机,恢复时 Redo Log 会发现自己处于 "Prepare" 状态,等待 Binlog 的最终判断。

阶段二:Commit 提交阶段

Step 2.1:写入 Binlog(Server 层操作)
  1. 写入 Binlog:Server 层开始处理,将这个事务对应的 Binlog 事件(SQL 语句或行变更)写入到 Binlog 文件中。
  2. binlog刷盘: 此时,Binlog 也被强制刷盘。
  3. 记录 XID: 在 Binlog 事件中,同样会记录这个事务的 XID
状态: Redo Log(Prepare 状态)和 Binlog 都已持久化到磁盘。Binlog 成为崩溃恢复的最终裁决者。
Step 2.2:写入 Redo Log Commit 标记(InnoDB 层操作)
  1. Redo Log 提交: 事务执行层通知 InnoDB 存储引擎,事务已完成 Binlog 写入。
  2. 最终标记: InnoDB 只需要在 Redo Log 上添加一个 "Commit" 标记(最终完成状态),表明该事务已彻底提交。这个操作通常只在内存中修改状态,并不强制刷盘。

状态: 事务彻底完成。数据修改对外部可见。

为什么 Step 2.2 不强制刷盘?

因为即使在 Step 2.2 标记完成前宕机,我们回到 Step 2.1 的状态:Redo Log (Prepare) + Binlog (已记录)。崩溃恢复时,MySQL 依然会根据 Binlog 来完成提交。所以,Step 2.2 的标记只是为了事务的正常结束流程,在崩溃恢复中不再是决定性的步骤。

崩溃恢复时恢复

2PC 的价值体现在崩溃恢复时。MySQL 重启时,会根据 Redo Log 和 Binlog 的状态,执行以下判断逻辑:

Redo Log 状态Binlog 状态恢复操作
未 Prepare无记录回滚 :事务视为失败,Redo Log 没有任何记录,直接清理。
Prepare 状态对应 XID 的记录提交 :事务视为成功,强制完成 Redo Log 的 Commit 标记(以 Binlog 为准)。
Prepare 状态对应 XID 的记录回滚 :事务视为失败,因为 Binlog 缺失,所以从库无法复制,主库需要回滚。
Committed 状态有记录跳过:事务已完成,正常运行。

undolog

回滚日志

逻辑日志,顾名思义,用来回滚的,也就是保障事务的原子性的作用。

记录修改之前的状态

Undo Log 的作用

作用一:保障事务的**原子性,回滚

实现机制:

  • 当一个事务执行 ROLLBACK 命令,或者事务执行过程中遭遇异常中断(如死锁被终止、应用程序崩溃)时,系统会利用 Undo Log 记录的逻辑信息,将数据恢复到事务开始前的状态。
  • 回滚流程: 沿着当前数据行上的 Roll_Pointer 指针,找到最新的 Undo Log 记录,执行其中的反向操作,从而撤销本次事务的所有修改。

作用二:实现多版本并发控制 (MVCC)

见事务篇

刷盘

在事务没提交之前,先把更新前的数据放到undo log 中,详细就是:

插入:记录插入的主键值就行--------回滚的时候只删除这个主键值对应的记录就行

删除:记录这个数据项的所有字段---回滚的时候把内容组成的记录重新插入到表中

更新:记录主键值和被修改的旧值---回滚的时候把列更新为旧值就行

随 Redo Log 刷盘而持久化。因为undolog本身就算是一个修改操作,修改了

异同

redo log 记录了此次事务「完成后」的数据状态,记录的是更新之后的值(持久性)

undo log记录了此次事务「开始前」的数据状态,记录的是更新之前的值(原子性)