mysql-日志

30 阅读5分钟

一、生成文件

InnoDB

  • 生成**xxx.ibd**文件,用于存储所有数据库对象的元数据。比如数据信息,索引信息

  • ibdata1

  • 日志: undo_*.log , #ib_redo*。从ibdata1中独立出来了

MyISAM

  • *.MYD:存储所有数据行

  • *.MYI:存储所有索引

  • *.sdi:用于存储非 InnoDB 表的元数据

二、日志操作

  • undo-log:主要用于实现事务ACID原则中的原子性和MVCC机制。
  • redo-log:主要用于实现事务原则中的持久性,确保事务提交后就不会丢失。
  • bin-log:主要结合redo-log实现事务原则中的一致性,确保事务提交前后,数据的一致。

1️⃣ Undo Log(回滚日志)

作用:记录事务修改前的旧值,用于事务回滚,同时支持 MVCC 快照读。

  • 逻辑层面的日志,关注“如何还原数据”,不会被其他事务看到。

案例 1:UPDATE

UPDATE user SET name='B' WHERE id=1;

假设表 user 原数据:

idname
1A

Undo Log 记录内容(逻辑示例)

事务ID: 100
操作类型: UPDATE
表: user
行主键: 1
旧值: name='A'
DB_ROLL_PTR: null

作用:如果事务回滚,将 name'B' 还原成 'A'


案例 2:DELETE

DELETE FROM user WHERE id=2;

假设删除前数据:

idname
2C

Undo Log 记录内容

事务ID: 101
操作类型: DELETE
表: user
行主键: 2
旧值: id=2, name='C'
DB_ROLL_PTR: null

作用:回滚时会重新插入这行数据。


案例 3:SELECT

SELECT * FROM user WHERE id=1;

  • Undo Log 不生成,因为没有修改数据。
  • 对于快照读,如果读到未提交版本,它会利用已存在的 Undo Log 回溯到可见版本。

2️⃣ Redo Log(重做日志)

作用:记录物理修改操作,用于崩溃恢复。

  • 顺序写磁盘,保证持久性。
  • 关注“这页的哪些字节被改成什么值”,粒度比 Undo 更接近页级或行级的物理存储。

案例 1:UPDATE

Redo Log 内容示意(逻辑化):

LSN: 120
事务ID: 100
表: user
页号: 7
行偏移: 1024
修改: name 列从 'A' 改为 'B'

意义:我对磁盘的第7页的1024行的数据进行了修改。后面恢复的时候直接去修改磁盘就行,更快

作用:数据库崩溃后,重做这个操作,把页的内容改回 'B'


案例 2:DELETE

Redo Log 内容示意:

LSN: 130
事务ID: 101
表: user
页号: 9
行偏移: 2048
删除操作:删除 id=2 行

作用:崩溃后重放日志即可把行从页中删除。


案例 3:SELECT

  • Redo Log 不生成,因为没有修改磁盘页。

3️⃣ Binlog(二进制日志)

作用:记录逻辑 SQL 操作,主要用于主从复制和备份恢复。

  • 粒度是 SQL 或行级逻辑(取决于 binlog_format:STATEMENT / ROW / MIXED)。
  • 作用对象是 全局可见数据,Undo/Redo 不关心的。

案例 1:UPDATE

UPDATE user SET name='B' WHERE id=1;

  • Row 格式 binlog 记录:

表: user 行ID: 1 旧值: name='A' 新值: name='B'

  • Statement 格式 binlog 记录:

UPDATE user SET name='B' WHERE id=1;


案例 2:DELETE

DELETE FROM user WHERE id=2;

  • Row 格式 binlog

表: user
行ID: 2
操作: DELETE
旧值: id=2, name='C'

  • Statement 格式 binlog

DELETE FROM user WHERE id=2;


案例 3:SELECT

  • Binlog 不生成,因为 SELECT 不修改数据。
  • 即便使用审计日志,也属于独立日志,不算 redo/undo/binlog 范畴。

🔑 总结对比

日志类型粒度作用生成时机示例作用
Undo Log行级,逻辑回滚、MVCC修改数据前UPDATE前的旧值
Redo Log页级/行级,物理崩溃恢复修改内存后Buffer Pool修改的物理页
BinlogSQL / 行主从复制、备份提交时SQL语句或新旧行数据

可以把它想象成:

  • Undo Log:时间的倒带胶卷 → 回到修改前

  • Redo Log:物理重做录像 → 崩溃重放

  • Binlog:广播录像 → 其他数据库看到这个操作

三、日志写入顺序

1️⃣ 日志写入过程:

1️⃣ 执行SQL语句,修改Buffer Pool中的页(内存中)
2️⃣ 记录Undo Log(为回滚准备)
3️⃣ 生成Redo Log(记录物理修改)
4️⃣ Redo Log写入磁盘的log buffer(可能异步flush)
5️⃣ 当执行COMMIT:
       a. Redo Log刷盘(持久化)
       b. Binlog写入binlog cache
       c. Binlog刷盘
       d. Redo标记为commit

2️⃣ 崩溃恢复:用时间线直观表示

时间线 →
 ┌────────────────────────────────────────────────────────────┐
 │                         MySQL 事务提交过程                  │
 └────────────────────────────────────────────────────────────┘
    ↓                ↓                 ↓
 [1] 写redo(prepare)→[2]写binlog→[3]redo标记commit
┌──────────────────────────────┬──────────────────────────────┬──────────────────────────────┐
│ 宕机点1:redo prepare后崩溃  │ 宕机点2:binlog写完崩溃     │ 宕机点3:redo commit后崩溃  │
├──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤
│ redo = prepare               │ redo = prepare               │ redo = commit                │
│ binlog = 无                  │ binlog = 有                  │ binlog = 有                  │
├──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤
│ 恢复时:回滚                 │ 恢复时:提交(重做)         │ 恢复时:已完成               │
│ 原因:无 binlog,认为未提交  │ 原因:有 binlog,推断成功提交│ 原因:redo commit 已持久化   │
└──────────────────────────────┴──────────────────────────────┴──────────────────────────────┘

3️⃣为什么需要redo+binlog双重验证

  1. 考虑主从一致性
    来看这个经典时间线:

    1️⃣ 写 redo log (prepare) 并刷盘 ✅
    2️⃣ 写 binlog,但写到一半宕机 ❌
    

    如果让 “prepare = commit”,那重启后 MySQL 就会:

    1. 根据 redo log 把修改数据重做出来;
    2. 但是 binlog 没写完(或没写入),
    3. 主库数据更新了,从库没有同步;
    4. 这就出现 主从不一致

    因此:

    prepare 不是“提交完成”,而是“随时可以提交,也可以回滚”的安全点。

    只有当 binlog 也写成功了,InnoDB 才把 redo 的状态改成 commit

  2. Q:为什么不能先写binlog,再写redo

    考虑到binlog是sever层,redo是引擎层的,那么完全可以当命令传到的时候就先进行记录。

    但是这样会出现另一个灾难场景:
    1️⃣ binlog 已写完 ✅
    2️⃣ redo log 还没写入磁盘 ❌(宕机)
    结果是:

    1. Binlog 说:“我提交成功了”
    2. Redo log 说:“我没保存修改”
    3. 主从同步执行时,从库照着 binlog 重放一遍;
    4. 但主库自己丢失了数据。主从再次不一致。