MySQL高级讲座篇之:MySQL崩溃恢复:`Redo Log`与`Undo Log`的恢复之旅。

各位观众老爷们,大家好!今天咱们聊点刺激的,聊聊MySQL的“起死回生术”——崩溃恢复。

想象一下,你正在玩一个紧张刺激的游戏,眼看就要通关了,突然电脑蓝屏了!心态崩了?别急,MySQL也有可能遇到类似的情况,比如服务器突然断电、系统崩溃等等。但MySQL可比咱们的游戏聪明多了,它有两大法宝:Redo Log(重做日志)和 Undo Log(撤销日志),能让它在崩溃后恢复到安全状态,最大程度地减少数据丢失。

今天咱们就来一场“Redo Log”与“Undo Log”的恢复之旅,看看MySQL是如何靠它们起死回生的。

一、什么是Redo Log?为什么要它?

简单来说,Redo Log就是MySQL记录“我要做什么”的日记本。它记录的是物理级别的修改,也就是“把哪个页面的哪个字节改成什么”。

  • 为什么需要Redo Log?

假设你执行一个更新语句:UPDATE users SET age = 30 WHERE id = 1;

这个操作可能涉及到多个步骤:

  1. 找到 id = 1 的那一行数据所在的页。
  2. 修改该页面的数据。
  3. 将修改后的页面刷盘(写入磁盘)。

问题来了:如果数据库在第2步完成,但第3步还没来得及做的时候崩溃了,那数据就丢失了,因为内存中的修改还没持久化到磁盘上。

这时候,Redo Log就派上用场了。在修改数据之前,MySQL会先将本次修改的日志写入Redo Log,然后再去修改内存中的数据。这样,即使数据库崩溃,重启后也能通过Redo Log将未完成的修改重新执行一遍,保证数据的完整性。

  • Redo Log 的特点:

    • 循环写入: Redo Log 是一组固定大小的文件,采用循环写入的方式。当写满时,会覆盖最早的日志记录。
    • 顺序IO: Redo Log 采用顺序IO,写入速度非常快。
    • 物理日志: 记录的是物理级别的修改,例如 "page X offset Y 改成 Z"。
  • Redo Log 长什么样?

    虽然我们不能直接打开Redo Log文件看到里面的内容,但我们可以想象它大概是这样的:

    Log Sequence Number (LSN) Table ID Page Number Offset Length Data
    1001 5 10 128 4 30 // 修改了Table ID为5的第10页,偏移量为128的位置,长度为4个字节,修改为30
    1002 5 10 132 4 ‘Bob’ // 修改了Table ID为5的第10页,偏移量为132的位置,长度为4个字节,修改为’Bob’

    LSN(Log Sequence Number)是一个递增的序列号,用于标识 Redo Log 的顺序。

  • Redo Log 相关参数

    以下是一些重要的Redo Log相关参数,可以通过SHOW GLOBAL VARIABLES LIKE '%innodb_log%';查看:

    参数名称 描述
    innodb_log_file_size 每个Redo Log文件的大小。
    innodb_log_files_in_group Redo Log文件的数量。通常设置为2或3。
    innodb_log_buffer_size Redo Log 缓冲区的大小。InnoDB会将Redo Log先写入缓冲区,然后再批量刷盘。
    innodb_flush_log_at_trx_commit 控制Redo Log的刷盘策略。
  • innodb_flush_log_at_trx_commit 的三种策略:

    • 0: 每秒将Redo Log缓冲区的内容刷盘一次。即使事务提交了,也可能没有立即刷盘。
    • 1: 每次事务提交时,都将Redo Log缓冲区的内容刷盘。这是最安全的策略,但性能也最差。
    • 2: 每次事务提交时,将Redo Log缓冲区的内容写入操作系统缓冲区,然后由操作系统决定何时刷盘。

    通常建议设置为 1,以保证数据的安全性。

二、什么是Undo Log?为什么要它?

Undo Log 记录的是修改前的状态,也就是“我要怎么撤销”。它记录的是逻辑级别的修改,也就是“把哪个页面的哪个记录改成什么”。

  • 为什么需要Undo Log?

假设你执行一个更新语句:UPDATE users SET age = 30 WHERE id = 1;

如果更新过程中出现错误,例如唯一约束冲突,或者你执行了 ROLLBACK 语句,就需要将数据恢复到修改前的状态。这时候,Undo Log 就派上用场了。

MySQL会根据 Undo Log 中记录的信息,将数据恢复到原始状态,保证事务的原子性。

  • Undo Log 的特点:

    • 保证事务的原子性: 用于回滚事务,将数据恢复到修改前的状态。
    • 逻辑日志: 记录的是逻辑级别的修改,例如 "将 id = 1 的 age 改回 25"。
    • 可能需要多次写入: 一个事务可能对应多个 Undo Log 记录。
  • Undo Log 长什么样?

    同样,我们不能直接看到 Undo Log 文件的内容,但可以想象它大概是这样的:

    Transaction ID Table ID Page Number Row ID Original Data
    123 5 10 1 {age: 25, name: ‘Alice’} // 事务ID为123的事务,修改了Table ID为5的第10页,Row ID为1的行的原始数据
  • Undo Log 存储位置

    • 在MySQL 5.6之前,Undo Log 只能存储在共享表空间 (ibdata1) 中。
    • 从MySQL 5.6开始,Undo Log 可以存储在独立的Undo表空间中,通过 innodb_undo_tablespaces 参数配置Undo表空间的数量。
  • Undo Log 相关参数

    • innodb_undo_tablespaces: 用于配置Undo表空间的数量,默认值为0,表示Undo Log存储在共享表空间中。如果设置为大于0的值,则表示使用独立的Undo表空间。
    • innodb_undo_directory: 用于指定Undo表空间的存储目录。

三、Redo Log 和 Undo Log 的协同工作

Redo LogUndo Log 就像一对黄金搭档,共同保证了MySQL的数据一致性。

当执行一个事务时,MySQL会:

  1. 将要修改的数据的原始状态写入 Undo Log
  2. 将要做的修改写入 Redo Log
  3. 修改内存中的数据。
  4. 提交事务。
  5. 根据 innodb_flush_log_at_trx_commit 的设置将 Redo Log 刷盘。
  6. 在适当的时候将修改后的数据刷盘。
  • 崩溃恢复过程:

    1. 分析 Redo Log: MySQL会扫描 Redo Log,找到所有未完成的事务。
    2. 重做未完成的事务: 对于已经提交但尚未刷盘的事务,MySQL会根据 Redo Log 将其重做,保证数据的持久性。
    3. 回滚未提交的事务: 对于尚未提交的事务,MySQL会根据 Undo Log 将其回滚,保证事务的原子性。

四、一个更形象的比喻

咱们再来打个比方:

  • Redo Log: 就像是饭店的“点菜记录”。厨师根据点菜记录,保证每一道菜都能做出来(即使客人突然断电跑路了,饭店也能根据点菜记录把菜做出来,然后找他要钱)。
  • Undo Log: 就像是饭店的“退菜记录”。如果客人突然说“这道菜我不想要了”,厨师可以根据退菜记录,把菜恢复到原来的状态(比如把已经切好的葱花放回葱里)。

五、实战演练:模拟崩溃恢复

为了更好地理解 Redo LogUndo Log 的作用,我们可以通过模拟崩溃来观察MySQL的恢复过程。

准备工作:

  1. 关闭自动提交: SET autocommit = 0;
  2. 创建一个测试表:

    CREATE TABLE test_recovery (
        id INT PRIMARY KEY,
        name VARCHAR(255)
    );
  3. 插入一条数据:

    INSERT INTO test_recovery (id, name) VALUES (1, 'Original Name');

模拟崩溃:

  1. 开启一个事务: START TRANSACTION;
  2. 修改数据: UPDATE test_recovery SET name = 'New Name' WHERE id = 1;
  3. 在事务提交之前,手动停止MySQL服务器。 (例如使用 sudo systemctl stop mysql)。

恢复过程:

  1. 启动MySQL服务器。 (例如使用 sudo systemctl start mysql)
  2. 连接到数据库。
  3. 查询数据: SELECT * FROM test_recovery WHERE id = 1;

观察结果:

  • 如果 innodb_flush_log_at_trx_commit 设置为 1,那么你会发现数据已经被修改为 ‘New Name’,因为Redo Log已经刷盘,MySQL在启动时会重做该事务。
  • 如果 innodb_flush_log_at_trx_commit 设置为 02, 并且Redo Log还没来得及刷盘, 那么你会发现数据仍然是 ‘Original Name’,因为MySQL在启动时会根据Undo Log回滚该事务。

代码示例:

-- 准备工作
SET autocommit = 0;

CREATE TABLE test_recovery (
    id INT PRIMARY KEY,
    name VARCHAR(255)
);

INSERT INTO test_recovery (id, name) VALUES (1, 'Original Name');

-- 模拟崩溃
START TRANSACTION;

UPDATE test_recovery SET name = 'New Name' WHERE id = 1;

-- 此时手动停止MySQL服务器

-- 恢复过程
-- 启动MySQL服务器

-- 连接到数据库
-- 查询数据
SELECT * FROM test_recovery WHERE id = 1;

-- 观察结果

六、总结

Redo LogUndo Log 是MySQL保证数据一致性的两大基石。Redo Log 用于重做已提交的事务,保证数据的持久性;Undo Log 用于回滚未提交的事务,保证事务的原子性。

理解 Redo LogUndo Log 的工作原理,可以帮助我们更好地理解MySQL的事务机制,从而写出更高效、更可靠的SQL语句。

希望通过今天的讲解,大家对MySQL的崩溃恢复机制有了更深入的了解。下次遇到MySQL崩溃的情况,就可以淡定地告诉你的同事:“别慌,有Redo Log和Undo Log呢!”

好了,今天的讲座就到这里,感谢大家的观看! 咱们下期再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注