开发需了解的知识:MySQL RedoLog/UndoLog具体存储内容

318 阅读13分钟

在数据库中,Redo LogUndo Log 是两种日志机制,分别用于不同的目的,保障数据的一致性、完整性和恢复能力。

1. 定义和作用

  • Redo Log(重做日志):
    Redo Log 是记录已经提交的事务对数据库的修改操作,主要用于崩溃恢复。在数据库出现故障时,Redo Log 能帮助数据库重做(恢复)已经提交但尚未写入数据文件的操作,确保数据一致性。
  • Undo Log(回滚日志):
    Undo Log 记录未提交事务的反向操作,用于事务回滚。如果某个事务需要回滚(比如用户取消了操作或出现错误),数据库可以通过 Undo Log 恢复到事务之前的状态,确保数据库的一致性。

2. 记录内容

  • Redo Log:
    Redo Log 记录的是对数据的物理修改,即某个事务对数据页的具体修改内容。这种日志记录的是如何将数据库修改成新的状态,通常是针对已提交事务的变化。
  • Undo Log:
    Undo Log 记录的是相反操作,即撤销某个未提交事务所做的修改。例如,如果事务进行了数据插入操作,Undo Log 会记录删除该数据的操作。它保存的是事务在数据库中的初始状态,方便在需要时进行回滚。

3. 触发时机

  • Redo Log:
    Redo Log 会在事务修改数据时被立即写入,用于记录这些修改操作。即使数据尚未真正写入磁盘,Redo Log 也已经记录了这些变更,以便在数据库崩溃后能够重新执行这些修改。
  • Undo Log:
    Undo Log 在事务开始时就开始记录每一步操作的反向操作,确保在事务回滚时可以正确恢复到事务之前的状态。这是为了保证事务的原子性。

4. 恢复场景

  • Redo Log:
    当数据库崩溃或意外关闭时,通过 Redo Log 进行恢复。它可以将已提交的事务重新应用到数据库中,即使这些修改还未写入磁盘。
  • Undo Log:
    当事务被取消或出现错误时,通过 Undo Log 进行回滚操作。它可以将数据库恢复到事务开始之前的状态,保证数据的完整性。

5. 性能影响

  • Redo Log:
    Redo Log 通过记录已经提交事务的修改操作,减少了频繁写入数据文件的压力,因此可以提高数据库的写入性能。然而,为保证事务持久性,Redo Log 需要快速写入。
  • Undo Log:
    Undo Log 需要记录每个未提交事务的逆操作,回滚操作越多,Undo Log 所占用的空间就越大。当回滚事务增多时,数据库性能可能受到影响。

6. 数据持久性

  • Redo Log:
    确保提交的事务即使在数据库崩溃后也能恢复,体现了事务的持久性(Durability)。数据库会先写入 Redo Log,再提交事务,以确保数据不会丢失。
  • Undo Log:
    确保事务未提交前,所有的操作都可以撤销,体现了事务的原子性(Atomicity)。通过 Undo Log,未提交的事务可以被撤回,不会影响数据库的其他部分。

7. 使用场景

  • Redo Log:
    主要用于崩溃恢复。在系统故障或断电后,可以根据 Redo Log 重新执行已提交的事务操作,以保证数据的完整性。
  • Undo Log:
    主要用于事务回滚。当用户取消操作或出现事务冲突时,Undo Log 通过撤销未提交的修改,确保数据回到正确的状态。

总结

对比项Redo Log(重做日志)Undo Log(回滚日志)
作用崩溃恢复,重做已提交的事务修改事务回滚,撤销未提交的事务操作
记录内容数据修改的正向操作(物理变更)事务的反向操作(撤销步骤)
触发时机事务提交或数据修改时记录事务开始时记录逆操作
恢复场景系统崩溃后,恢复已提交数据事务失败或取消时,回滚未提交数据
数据持久性保证已提交事务的数据持久化保证事务的原子性,可撤销未提交操作
使用场景崩溃恢复,重做已提交修改事务回滚,撤销未提交的事务

简而言之,Redo Log 主要用于恢复已提交的事务,而 Undo Log 则用于回滚未提交的事务,共同保障了数据库的可靠性和一致性。

Redo Log具体记录内容

Redo Log 记录的是事务对数据库的物理修改,其主要目的是在数据库崩溃或异常时,帮助系统将已经提交但尚未持久化的数据恢复到最新状态。它的记录内容非常具体,主要包括事务的修改操作以及这些操作所涉及的数据库页、块等。为了更好地理解,以下是一个具体的例子:

场景假设

假设我们有一张名为 employees 的表,包含两列:idsalary。我们要执行一个事务,更新员工的薪水。

BEGIN;
UPDATE employees SET salary = salary + 500 WHERE id = 1001;
COMMIT;

Redo Log 记录的内容

当执行上述事务时,Redo Log 会记录事务对数据库的修改。具体的内容可以分为以下几个部分:

  1. 事务的开始标记: 在事务开始时,Redo Log 会记录一个事务开始的标记。它表明一个新的事务正在进行,通常包括事务的唯一标识符(事务ID)。

    例如:

    BEGIN TRANSACTION: TXID = 12345
    
  2. 物理修改操作记录: 当 SQL 更新操作被执行时,数据库会查找到目标数据所在的数据页(通常数据按页存储,每页固定大小),并且修改该页中相关数据的值。

    在这个例子中,事务要修改 employees 表中 id = 1001 的员工记录的 salary。数据库会定位到存储该记录的页面(假设是数据页 PAGE 5432),并修改该记录。

    Redo Log 会记录:

    • 修改的数据页ID(例如 PAGE 5432
    • 该页中被修改的具体字节偏移(表明是哪个字段被修改)
    • 旧值和新值(或者只记录新值,取决于具体的数据库实现)

    例如:

    MODIFY PAGE: PAGE 5432, OFFSET 200, OLD VALUE: 5000, NEW VALUE: 5500
    

    这表示在 PAGE 5432 的偏移量为 200 处,salary5000 被修改为 5500

  3. 事务的提交标记: 当事务执行到 COMMIT 时,Redo Log 会记录一个事务提交的标记,表示该事务已提交,所有的修改操作都是持久的。如果数据库崩溃,恢复机制会根据这个提交标记来重做该事务的修改。

    例如:

    COMMIT TRANSACTION: TXID = 12345
    

Redo Log 恢复场景

假设在该事务提交后,数据尚未写入磁盘,但数据库突然崩溃了。在重启数据库时,恢复机制会通过 Redo Log 进行如下操作:

  1. 通过 Redo Log 找到事务 TXID = 12345 的修改记录。
  2. 发现该事务已经提交,所以需要重做事务的修改。
  3. 根据 Redo Log 记录的内容,找到 PAGE 5432,并将偏移量 200 处的 salary 值恢复为 5500

Redo Log 记录的内容详解

Redo Log 具体记录的内容,通常包括以下信息:

  • 事务ID(Transaction ID, TXID): 用于唯一标识事务。
  • 操作类型: 包括事务开始、提交或修改数据页。
  • 表或页面的唯一标识符: 记录被修改的表或数据页的ID。
  • 数据页的偏移量: 被修改的具体数据位置,以字节偏移量表示。
  • 修改的内容: 包括修改前的旧值、修改后的新值(有时Redo Log只记录新值)。

总结

Redo Log 通过记录物理级别的修改(例如某个数据页的具体字节变化),保证了即使数据库发生崩溃,已提交的事务也不会丢失。它记录了事务从开始到提交的所有操作,确保数据库能够在崩溃后恢复一致性。

Undo Log 记录的是数据库中事务未提交的反向操作,它的主要目的是支持事务回滚。当事务在执行过程中出现问题或用户主动取消操作时,Undo Log 能帮助数据库撤销未提交的更改,将数据恢复到修改之前的状态。

Undo Log 的基本记录内容

Undo Log 记录的是事务进行的每一个修改前的旧值。通过这些旧值,在事务需要回滚时,数据库可以撤销未提交的修改,将数据恢复为修改前的状态。

举例说明

假设我们有一张名为 employees 的表,其中包含两列:idsalary。我们进行一个事务来更新某位员工的薪水,但在事务提交前发生了错误,导致需要回滚。

场景
BEGIN;
UPDATE employees SET salary = salary + 500 WHERE id = 1001;
-- 某种原因导致事务失败或被取消
ROLLBACK;
Undo Log 的记录过程
  1. 事务开始: 在事务开始时,数据库不会立即记录在 Undo Log 中,而是等待具体的修改操作发生。

  2. 修改操作的 Undo Log 记录: 当我们执行 UPDATE employees SET salary = salary + 500 WHERE id = 1001 时,数据库会先记录下修改前的数据,以便在回滚时可以还原。具体地,数据库会记录该员工的 salary 修改前的值,假设原来的 salary = 5000,修改后的新值是 5500

    Undo Log 会记录:

    • 修改前的旧值(即 salary = 5000
    • 修改的位置(包括数据页和偏移量,类似于 Redo Log 中的记录方式)

    示例记录如下:

    UNDO LOG: PAGE 5432, OFFSET 200, OLD VALUE: 5000
    

    这条记录表明,如果回滚,数据库应该将数据页 PAGE 5432 的偏移量 200 处的值恢复为 5000,即回滚前的薪水。

  3. 回滚操作: 当事务执行 ROLLBACK 时,数据库会查找 Undo Log,按照逆序撤销该事务做出的修改。这里,Undo Log 会根据记录,将员工的薪水从 5500 恢复到 5000,使数据库回到事务开始时的状态。

    回滚过程会依次撤销每一项操作。对于这个示例,数据库将会:

    • 找到 PAGE 5432,恢复偏移量 200 处的值为 5000,即回滚薪水的增加操作。
  4. 事务结束: 当事务完全回滚后,数据库会删除与该事务相关的 Undo Log 记录,以释放空间。

Undo Log 记录的内容详解

Undo Log 通常记录以下几项内容:

  • 事务ID(Transaction ID, TXID): 标识当前事务。
  • 操作类型: 记录修改类型(如 INSERT、UPDATE、DELETE)。
  • 表或数据页标识符: 记录被修改的表或页的标识符。
  • 修改的位置: 指出具体数据的位置(例如数据页ID、偏移量等)。
  • 旧值: 记录修改前的数据(用于回滚操作)。

更复杂的场景

假设我们进行一次插入操作,并且在之后回滚。Undo Log 在这种情况下也会记录下反向操作。

BEGIN;
INSERT INTO employees (id, salary) VALUES (1002, 3000);
ROLLBACK;

在这种场景下,Undo Log 会记录插入了新的记录。为了能够回滚,Undo Log 必须记录删除新插入记录的操作。也就是说,Undo Log 会记载:

UNDO LOG: DELETE ROW WHERE id = 1002

当事务回滚时,Undo Log 会执行这个删除操作,移除新插入的记录 id = 1002,使数据库回到插入前的状态。

总结

Undo Log 主要用于事务回滚,它记录了每个事务操作前的旧值或相应的反向操作。例如:

  • UPDATE 操作中,Undo Log 记录修改前的值。
  • INSERT 操作中,Undo Log 记录要删除新插入的行。
  • DELETE 操作中,Undo Log 记录要恢复的被删除数据。

Undo Log 使得数据库能够轻松地将未提交的事务进行回滚,恢复数据一致性,确保事务的原子性

思考题:假如插入了id=1002数据被回滚了,再次插入id是多少

如果插入了 id=1002 的数据后进行了回滚,那么该条记录实际上并没有持久化到数据库中,也就是说 id=1002 并不存在于数据库中。

当你再次插入一条记录时,id 的生成方式取决于主键生成策略,通常有以下几种情况:

1. 手动插入 id

如果 id 是由用户手动指定的,那么回滚后再次插入时,你可以再次使用 id=1002,因为之前的插入操作并未成功,1002 这个值并没有占用。例如:

INSERT INTO employees (id, salary) VALUES (1002, 3000);

在这种情况下,回滚后你可以再次使用 1002 作为 id,因为它从未真正被插入到数据库中。

2. 自增主键(AUTO_INCREMENT)

如果 id 是使用数据库的自增主键(如 MySQL 的 AUTO_INCREMENT),情况会有所不同。自增主键的值是由数据库自动生成的,并且一旦生成,即使回滚,主键计数器也不会回退。换句话说,即使事务回滚,自增主键生成的 id=1002 也已经占用了这个编号。

  • 第一次插入: id=1002 被分配,但由于回滚,记录被撤销。
  • 第二次插入: 自增主键会继续从上次分配的 id 之后进行编号,因此新的 id 会是 1003

例如,使用 AUTO_INCREMENT 时:

INSERT INTO employees (salary) VALUES (3000);

在回滚后,下一次插入的 id 会是 1003,而不是 1002,因为 1002 已经被分配过了,虽然事务被回滚了,但 AUTO_INCREMENT 不会重用该编号。

3. 其他主键生成策略

如果数据库使用了其他主键生成策略(如 PostgreSQL 的序列 SEQUENCE 或 UUID),结果也取决于策略的设计:

  • SEQUENCE 类似于 AUTO_INCREMENT,即一旦序列值被分配,即使事务回滚,序列也不会重用该值。
  • UUID 则是每次都会生成唯一的标识符,不受回滚影响,下一次插入时会生成一个全新的 UUID。

总结

  • 如果 id 是手动指定的,回滚后可以重新使用 1002
  • 如果 id 是由自增主键生成的,回滚后下一次插入的 id 会是 1003,而不是 1002,因为自增主键不会回退。
  • 其他主键策略(如序列、UUID)会根据各自的规则生成下一个唯一值。