在数据库中,Redo Log 和 Undo 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
的表,包含两列:id
和 salary
。我们要执行一个事务,更新员工的薪水。
sql
BEGIN;
UPDATE employees SET salary = salary + 500 WHERE id = 1001;
COMMIT;
Redo Log 记录的内容
当执行上述事务时,Redo Log 会记录事务对数据库的修改。具体的内容可以分为以下几个部分:
-
事务的开始标记: 在事务开始时,Redo Log 会记录一个事务开始的标记。它表明一个新的事务正在进行,通常包括事务的唯一标识符(事务ID)。
例如:
sqlBEGIN TRANSACTION: TXID = 12345
-
物理修改操作记录: 当 SQL 更新操作被执行时,数据库会查找到目标数据所在的数据页(通常数据按页存储,每页固定大小),并且修改该页中相关数据的值。
在这个例子中,事务要修改
employees
表中id = 1001
的员工记录的salary
。数据库会定位到存储该记录的页面(假设是数据页PAGE 5432
),并修改该记录。Redo Log 会记录:
- 修改的数据页ID (例如
PAGE 5432
) - 该页中被修改的具体字节偏移(表明是哪个字段被修改)
- 旧值和新值(或者只记录新值,取决于具体的数据库实现)
例如:
yamlMODIFY PAGE: PAGE 5432, OFFSET 200, OLD VALUE: 5000, NEW VALUE: 5500
这表示在
PAGE 5432
的偏移量为200
处,salary
从5000
被修改为5500
。 - 修改的数据页ID (例如
-
事务的提交标记: 当事务执行到
COMMIT
时,Redo Log 会记录一个事务提交的标记,表示该事务已提交,所有的修改操作都是持久的。如果数据库崩溃,恢复机制会根据这个提交标记来重做该事务的修改。例如:
sqlCOMMIT TRANSACTION: TXID = 12345
Redo Log 恢复场景
假设在该事务提交后,数据尚未写入磁盘,但数据库突然崩溃了。在重启数据库时,恢复机制会通过 Redo Log 进行如下操作:
- 通过 Redo Log 找到事务
TXID = 12345
的修改记录。 - 发现该事务已经提交,所以需要重做事务的修改。
- 根据 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
的表,其中包含两列:id
和 salary
。我们进行一个事务来更新某位员工的薪水,但在事务提交前发生了错误,导致需要回滚。
场景
sql
BEGIN;
UPDATE employees SET salary = salary + 500 WHERE id = 1001;
-- 某种原因导致事务失败或被取消
ROLLBACK;
Undo Log 的记录过程
-
事务开始: 在事务开始时,数据库不会立即记录在 Undo Log 中,而是等待具体的修改操作发生。
-
修改操作的 Undo Log 记录: 当我们执行
UPDATE employees SET salary = salary + 500 WHERE id = 1001
时,数据库会先记录下修改前的数据,以便在回滚时可以还原。具体地,数据库会记录该员工的salary
修改前的值,假设原来的salary = 5000
,修改后的新值是5500
。Undo Log 会记录:
- 修改前的旧值 (即
salary = 5000
) - 修改的位置(包括数据页和偏移量,类似于 Redo Log 中的记录方式)
示例记录如下:
yamlUNDO LOG: PAGE 5432, OFFSET 200, OLD VALUE: 5000
这条记录表明,如果回滚,数据库应该将数据页
PAGE 5432
的偏移量200
处的值恢复为5000
,即回滚前的薪水。 - 修改前的旧值 (即
-
回滚操作: 当事务执行
ROLLBACK
时,数据库会查找 Undo Log,按照逆序 撤销该事务做出的修改。这里,Undo Log 会根据记录,将员工的薪水从5500
恢复到5000
,使数据库回到事务开始时的状态。回滚过程会依次撤销每一项操作。对于这个示例,数据库将会:
- 找到
PAGE 5432
,恢复偏移量200
处的值为5000
,即回滚薪水的增加操作。
- 找到
-
事务结束: 当事务完全回滚后,数据库会删除与该事务相关的 Undo Log 记录,以释放空间。
Undo Log 记录的内容详解
Undo Log 通常记录以下几项内容:
- 事务ID(Transaction ID, TXID): 标识当前事务。
- 操作类型: 记录修改类型(如 INSERT、UPDATE、DELETE)。
- 表或数据页标识符: 记录被修改的表或页的标识符。
- 修改的位置: 指出具体数据的位置(例如数据页ID、偏移量等)。
- 旧值: 记录修改前的数据(用于回滚操作)。
更复杂的场景
假设我们进行一次插入操作,并且在之后回滚。Undo Log 在这种情况下也会记录下反向操作。
sql
BEGIN;
INSERT INTO employees (id, salary) VALUES (1002, 3000);
ROLLBACK;
在这种场景下,Undo Log 会记录插入了新的记录。为了能够回滚,Undo Log 必须记录删除新插入记录的操作。也就是说,Undo Log 会记载:
sql
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
这个值并没有占用。例如:
sql
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
时:
sql
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)会根据各自的规则生成下一个唯一值。