数据库基础体系 · 第 107/139 篇。文章以各产品官方稳定版本的公开语义为准;示例会明确引擎、事务与部署边界。
Oracle Redo、归档与 Checkpoint:提交、恢复和写入路径
在 Oracle 中,“事务已经提交”“数据块已经写入数据文件”“日志已经归档”“数据库已经能够从故障中恢复”是四个不同状态。它们经常同时出现,却不能互相替代。
理解这几个状态,需要把一条修改从内存中的数据块开始,沿着以下路径追踪:
SQL 执行
├─ 修改 Buffer Cache 中的数据块
├─ 修改 Undo 段中的撤销信息
└─ 生成 Redo,写入 Log Buffer
│
├─ 提交时由 LGWR 写入 Online Redo Log
├─ 日志切换后由 ARCn/归档进程复制为 Archived Redo Log
└─ Checkpoint 触发 DBWn 将脏数据块写入数据文件
恢复时方向相反:
数据文件或实例故障
├─ 从 Checkpoint 之后的 Redo 重做已发生的修改
├─ 根据事务提交记录判断哪些事务已经提交
└─ 对未提交事务使用 Undo 回滚
下面分别解释这些组件、状态和故障路径。
一、先建立几个边界:Redo、Undo、数据文件不是同一种东西
1. Redo 是“如何重做修改”的日志
Redo 记录的是数据库为了恢复而需要的信息。它通常描述了某个数据块、Undo 块或其他数据库结构发生了什么变化,使恢复进程能够把修改重新应用到相应位置。
Redo 不是 SQL 文本日志。例如,下面的语句:
UPDATE accounts
SET balance = balance - 100
WHERE account_id = 1;
Redo 中并不要求保存完整的 SQL 字符串。恢复需要的是足以重建块修改的内部记录。
Redo 可能包含:
- 表数据块的修改;
- 索引块的修改;
- Undo 段和 Undo 块的修改;
- 数据字典或其他内部结构的修改;
- 事务提交等事务状态信息。
因此,Undo 本身也需要 Redo 保护。否则,数据库可以重做表数据块,却无法可靠地重建事务的撤销信息。
2. Undo 是“如何撤销修改”的信息
Undo 主要服务于两个目标:
- 事务回滚;
- 读一致性。
假设事务把某行从 100 改成 50,Oracle 可以在当前块中看到新值,同时把旧值 100 保存在 Undo 中。一个较早 SCN 的查询可以通过 Undo 构造出它应该看到的旧版本。
Redo 和 Undo 的方向不同:
| 机制 | 主要问题 | 典型用途 |
|---|---|---|
| Redo | 如何把修改重新做出来 | 实例恢复、介质恢复、Data Guard |
| Undo | 如何撤销修改或构造旧版本 | 回滚、读一致性 |
一个常见误解是:“有 Undo,所以重启后可以恢复事务。”不准确。实例恢复首先依赖 Redo 将数据文件推进到故障前状态,然后再根据事务状态和 Undo 清理未提交事务。Undo 不是 Redo 的替代品。
3. 数据块写入数据文件不等于事务提交
Oracle 使用 Buffer Cache。服务器进程通常先修改内存中的数据块,形成脏块;DBWn 在之后把脏块写入数据文件。
因此可能出现以下状态:
数据块已经写入数据文件,但事务尚未提交
这并不违反事务原子性。恢复时,如果发现该事务没有提交记录,就会使用 Undo 将它撤销。
也可能出现相反状态:
事务已经提交,但相应数据块还没有写入数据文件
这同样是正常的。只要提交对应的 Redo 已经持久化,恢复时就可以从 Redo 重做数据块修改。
这正是 Oracle 写前日志(Write-Ahead Logging,WAL)原则的核心:
在允许脏数据块写入数据文件之前,描述这些修改的 Redo 必须先写入持久化的 Redo Log。
二、Oracle 的三类主要持久化位置
1. Log Buffer
Log Buffer 位于 SGA 中,用于暂存各服务器进程生成的 Redo。
执行 DML 时,服务器进程通常完成以下动作:
- 在 Buffer Cache 中找到或读入数据块;
- 在需要时生成 Undo;
- 修改数据块和 Undo 块;
- 生成相应 Redo;
- 把 Redo 放入 Log Buffer。
Log Buffer 是内存,实例崩溃时其中尚未写出的内容会丢失。因此,事务提交不能仅依赖 Log Buffer。
2. Online Redo Log
Online Redo Log 是持久化的循环日志,由多个 redo log group 组成;每个 group 通常又包含一个或多个 member,以提供日志文件级别的镜像保护。
逻辑上,Oracle 在日志组之间循环使用:
GROUP 1 → GROUP 2 → GROUP 3 → GROUP 1 → ...
当前正在写入的组称为 CURRENT。已经写满并切换出去的组可能成为:
ACTIVE:其中的 Redo 仍可能被实例恢复需要;INACTIVE:满足条件后可以被重新使用。
这里的状态不是“是否已经提交”的事务状态,而是日志组相对于数据库恢复和写入位置的状态。
3. Archived Redo Log
在 ARCHIVELOG 模式下,Oracle 在 Online Redo Log 发生日志切换后,把已填充的日志组复制为归档日志。归档日志通常由 ARCn 进程或相关归档机制完成。
归档日志是 Online Redo Log 的持久历史副本,不是另一种事务日志格式。它的主要价值是:
- 保留已经被 Online Redo Log 覆盖的历史 Redo;
- 支持从备份进行介质恢复;
- 支持 RMAN 恢复;
- 支持 Data Guard 的日志传输和应用。
归档发生在日志切换路径上,而不是每次 COMMIT 时单独生成一个归档文件。
三、一次 DML 到提交:完整写入路径
考虑以下事务:
UPDATE accounts
SET balance = balance - 100
WHERE account_id = 1;
UPDATE accounts
SET balance = balance + 100
WHERE account_id = 2;
COMMIT;
设事务开始前,数据库中的相关状态如下:
账户 1:1000
账户 2:500
第一步:读取并修改 Buffer Cache 中的数据块
如果包含 account_id = 1 的数据块不在 Buffer Cache,服务器进程先从数据文件读入;之后在内存中修改它:
数据文件:账户 1 = 1000
Buffer Cache:账户 1 = 900,状态为脏块
对于账户 2 也可能发生同样的过程。
此时,数据文件可能仍然是旧值。事务也还没有提交。
第二步:生成 Undo
为了支持回滚和读一致性,Oracle 需要保存旧版本信息:
Undo:
账户 1 的旧值为 1000
账户 2 的旧值为 500
Undo 块也可能位于 Buffer Cache 中,并且变成脏块。
第三步:生成 Redo
Oracle 为数据块和 Undo 块的变化生成 Redo:
Redo:
修改账户 1 所在数据块
修改账户 2 所在数据块
修改相关 Undo 块
此时这些 Redo 可能只在 Log Buffer 中。
第四步:执行 COMMIT
默认情况下,提交需要等待 LGWR 将该事务的提交相关 Redo 写入 Online Redo Log。提交记录以及该事务相关的 Redo 持久化后,Oracle 才向客户端返回提交成功。
可以把提交的关键条件形式化为:
COMMIT 返回成功
⇒ 提交所需 Redo 已经交给操作系统并完成持久化写出
这里的“持久化”还依赖存储系统是否正确履行写入和缓存刷新语义。数据库可以要求操作系统和存储设备完成持久化,但如果硬件或虚拟化层错误地伪报写入完成,数据库软件无法弥补该故障。
提交时通常不要求 DBWn 同时把相关数据块写入数据文件:
提交成功:
Redo 已持久化
数据块可能仍在 Buffer Cache
第五步:稍后由 DBWn 写数据块
DBWn 在多种条件下写出脏块,例如:
- Checkpoint 的写入要求;
- Buffer Cache 空间压力;
- 脏块过多;
- 数据库后台写入调度;
- 数据文件或表空间相关写入需求。
于是最终可能变成:
Online Redo Log:包含并已持久化事务 Redo
数据文件:包含账户 1 = 900、账户 2 = 600
如果此时数据库崩溃,恢复仍然要依赖 Redo 判断数据文件缺少哪些修改。
四、LGWR:为什么 COMMIT 通常等待日志写出
LGWR(Log Writer)负责把 Log Buffer 中的 Redo 写入 Online Redo Log。它不仅在提交时工作,还可能因为以下原因被唤醒:
- 事务提交;
- Log Buffer 达到一定使用程度;
- DBWn 准备写入某个脏块,但对应 Redo 尚未写出;
- 日志写入超时或后台调度;
- 日志切换需要;
- 其他内部恢复和日志管理需求。
1. 提交等待的是 Redo,不是数据块
错误的因果关系是:
COMMIT
→ DBWn 写数据文件
→ 数据文件安全
更准确的关系是:
COMMIT
→ LGWR 写提交相关 Redo
→ 提交成功返回
→ DBWn 以后再写数据块
这使得提交路径不必同步等待随机数据块写入。Redo 通常是顺序追加写,适合集中写出。
2. Group Commit
多个会话可能在相近时间提交。LGWR 可以一次写出多个事务的 Redo,这称为组提交(group commit)。
例如:
会话 A:提交,等待 LGWR
会话 B:提交,等待 LGWR
会话 C:提交,等待 LGWR
LGWR 一次写出包含 A、B、C 提交记录的日志范围后,三个会话都可能完成等待。
组提交并不改变事务语义:
- 每个事务仍有自己的提交顺序和提交记录;
- 某个事务的提交成功仍要求它所需的 Redo 已持久化;
- 组提交只是把多个写请求合并。
3. COMMIT WRITE 的边界
Oracle 支持通过 COMMIT WRITE 指定提交写入行为,例如:
COMMIT WRITE WAIT IMMEDIATE;
常见语义是:
WAIT:等待 LGWR 完成必要的日志写出后再返回;NOWAIT:不等待日志写出就返回;IMMEDIATE、BATCH:影响日志写请求的调度方式。
默认行为和具体可用选项应以目标 Oracle 版本和初始化参数为准。NOWAIT 不是“更快但完全等价”的提交。它允许客户端在 Redo 尚未持久化时就收到提交返回,因此发生实例故障时,刚刚返回成功的事务可能丢失。
这类提交通常不应作为一般业务事务的默认选择。它不是把数据库变成非事务系统,而是改变了客户端获得“提交成功”通知与日志持久化之间的等待边界。
五、Checkpoint:推进恢复起点,而不是“保存所有数据”
1. Checkpoint 的定义
Checkpoint 是数据库建立的一个恢复进度点。它通常包含一个 SCN 边界,表示:
在该边界之前,相关数据块已经按照恢复要求写入数据文件,恢复可以从更接近该位置的 Redo 开始。
Checkpoint 的直接目标是减少实例恢复所需扫描的 Redo 范围,并协调数据文件、控制文件和日志位置之间的恢复信息。
Checkpoint 不是:
- 提交所有未提交事务;
- 把所有脏块瞬间写完;
- 删除 Checkpoint 之前的所有 Redo;
- 替代归档;
- 生成一个可独立恢复的备份。
2. Checkpoint 与 DBWn、CKPT 的关系
可以区分两个角色:
- CKPT:协调 Checkpoint,更新控制文件和数据文件头中的相关 Checkpoint 信息,并通知需要处理的组件;
- DBWn:将满足条件的脏数据块写入数据文件。
因此,Checkpoint 的数据流可以抽象为:
Checkpoint 请求
→ 确定目标 SCN 或相关范围
→ 通知 DBWn
→ DBWn 写出满足条件的脏块
→ 更新控制文件/数据文件头的恢复信息
现代 Oracle 的实际写入由多个后台进程、检查点队列和增量机制共同完成,不能简单理解为“CKPT 自己把所有数据写入文件”。
3. Checkpoint SCN 的直觉
假设某数据文件的 Checkpoint SCN 为 1000,表示恢复时至少需要考虑 1000 之后可能影响该文件的数据。
如果故障发生时数据库已经把相应数据块推进到 1500:
数据文件 Checkpoint SCN:1000
故障时需要恢复到:1500
那么恢复至少需要处理大致位于:
SCN 1000 之后到故障时刻
的 Redo。
Checkpoint 越新,通常实例恢复需要处理的 Redo 越少;但 Checkpoint 越频繁或写入压力越强,也可能增加 DBWn 的写入和 I/O 竞争。这里没有“越频繁越好”的绝对结论。
4. Checkpoint 不改变事务提交状态
下面的事务没有提交:
UPDATE accounts
SET balance = balance - 100
WHERE account_id = 1;
ALTER SYSTEM CHECKPOINT;
Checkpoint 可能促使相关数据块写入数据文件,但它不会把事务变成已提交事务。故障恢复时:
数据块中的未提交修改可以已经落盘
事务没有提交记录
恢复阶段使用 Undo 将其撤销
反例是把“数据块已经落盘”当作“事务已经安全完成”。这会错误地把物理写入状态当成事务状态。
六、日志切换、归档和日志组复用
1. 日志切换是什么
当当前 Online Redo Log group 使用到需要切换的位置,Oracle 选择下一个可用的日志组,使其成为新的 CURRENT。
典型状态变化类似:
切换前:
Group 1:CURRENT
Group 2:INACTIVE
Group 3:INACTIVE
切换后:
Group 1:ACTIVE 或等待归档
Group 2:CURRENT
Group 3:INACTIVE
具体状态还取决于实例恢复、归档和日志组配置。
日志切换不等于 Checkpoint:
- 日志切换改变当前写入的日志组;
- Checkpoint 推进数据文件的恢复进度;
- 日志切换可能触发或伴随 Checkpoint 活动,但两者概念不同。
2. 为什么不能立即复用旧日志组
Oracle 不能在旧日志组仍可能被实例恢复需要时覆盖它。通常需要满足与以下因素相关的条件:
- 该日志组中的 Redo 已经不再是实例恢复所必需的;
- 在
ARCHIVELOG模式下,所需归档已经完成; - 其他数据库状态和恢复约束允许复用。
如果归档目的地不可用,数据库可能无法安全复用日志组,最终表现为日志切换停滞,甚至出现无法分配新的日志组空间。
这是归档目录问题会反过来影响在线事务的原因:归档虽然是后台工作,但 Online Redo Log 是有限的循环空间。
3. 强制日志切换示例
在具备相应权限并确认环境允许时,可以观察日志切换:
SELECT log_mode, force_logging
FROM v$database;
SELECT group#, thread#, sequence#, status, archived
FROM v$log
ORDER BY thread#, group#;
ALTER SYSTEM SWITCH LOGFILE;
然后再次查询:
SELECT group#, thread#, sequence#, status, archived
FROM v$log
ORDER BY thread#, group#;
预期现象通常是:
CURRENT的 group 发生变化;- sequence number 递增;
- 原来的当前组变成其他状态;
- 在
ARCHIVELOG模式下,归档列和归档视图会随后反映进度。
“随后”很重要:日志切换完成与归档进程完成复制不是同一个瞬间。
可以查看数据库的归档模式:
SELECT log_mode
FROM v$database;
查看归档日志记录:
SELECT thread#, sequence#, first_change#, next_change#,
completion_time, deleted, status
FROM v$archived_log
ORDER BY thread#, sequence#;
这些视图通常需要查询目录对象的权限。生产环境中不要为了测试随意删除归档日志;归档日志是否可删除应由 RMAN 备份和恢复策略决定。
七、ARCHIVELOG 与 NOARCHIVELOG:恢复能力的根本差异
1. NOARCHIVELOG
在 NOARCHIVELOG 模式下,数据库通常只能依赖当前保留的 Online Redo Log 完成有限的实例恢复。数据库关闭后制作的备份可以用于某些恢复场景,但不能像归档模式那样将备份恢复到任意后续时间点。
其核心限制是:
旧的 Online Redo Log 被循环覆盖后,
数据库没有连续的历史 Redo 可用于介质恢复。
2. ARCHIVELOG
在 ARCHIVELOG 模式下,Online Redo Log 被切换出去后,可以保存为归档日志。于是可以形成:
数据文件备份
+ 一系列 Archived Redo Log
+ 必要时的 Online Redo Log
→ 将数据库恢复到备份之后的某个时间或 SCN
这也是 RMAN 时间点恢复、Data Guard 日志传输和许多生产恢复方案的基础。
但开启 ARCHIVELOG 并不自动意味着“备份可靠”:
- 归档目的地可能不可用;
- 归档文件可能损坏或丢失;
- 没有可用的基线备份时,连续 Redo 也无法独立重建整个数据库;
- 控制文件、参数文件、加密钱包等恢复所需内容也要纳入设计。
八、实例恢复:数据库崩溃后发生什么
实例恢复针对的是实例异常终止,例如主机断电或 Oracle 实例崩溃,但数据文件仍然可访问。
1. 为什么需要实例恢复
由于 DBWn 可以延迟写数据文件,故障发生时可能存在:
数据文件:
只包含部分已提交修改
也可能包含部分未提交修改
Online Redo:
包含故障前已经写出的 Redo
数据文件因此可能处于“模糊”状态,不能仅靠查看文件中的值判断事务结果。
2. 实例恢复的两个主要阶段
可以将过程抽象成:
阶段一:前滚(roll forward)
从适当的 Checkpoint 位置开始,读取 Online Redo,把故障前已经发生但尚未落入数据文件的修改重做。
Checkpoint SCN = 1000
故障时可用 Redo 到 SCN = 1600
恢复读取:
SCN 1000 → 1600
这里的目标不是“执行 SQL”,而是应用内部 Redo 记录,使数据块达到故障前应有的状态。
阶段二:回滚未完成事务
前滚可能把某些未提交事务的修改也重做了,因为这些修改在故障前确实已经发生。随后 Oracle 根据事务提交状态,使用 Undo 撤销没有提交记录的事务。
假设:
事务 T1:
修改已写入 Redo
提交记录也已写入 Redo
事务 T2:
修改已写入 Redo
但没有提交记录
恢复后:
T1 的修改保留
T2 的修改撤销
这解释了为什么恢复不能只做“把所有 Redo 重放一遍”。还必须判断事务是否完成。
3. 提交记录的作用
事务提交可以看作一个恢复判定点:
修改 Redo + 提交 Redo
→ 恢复后保留
只有修改 Redo,没有提交 Redo
→ 恢复后回滚
这不是说 Redo 中只有一条“提交日志”,而是说事务状态信息和修改记录共同决定恢复结果。
九、介质恢复:数据文件损坏时为什么需要归档日志
实例恢复假设数据文件仍在,只是其中的数据可能落后或包含未清理修改。
介质恢复针对更严重的情况,例如:
- 数据文件丢失;
- 数据文件损坏;
- 磁盘或存储故障;
- 需要把数据库恢复到某个时间点。
典型流程是:
1. 使用备份恢复数据文件
2. 数据文件回到备份时的较早状态
3. 应用备份之后的 Archived Redo Log
4. 必要时继续应用 Online Redo Log
5. 根据恢复目标执行介质恢复或时间点恢复
形式化地说,设数据文件备份对应的数据库状态为 SCN = 1000,目标是恢复到 SCN = 1500:
Backup(1000)
+ Redo(1000, 1500]
→ Database(1500)
如果中间缺少任意必要日志,例如只有:
Redo(1000, 1200]
Redo(1300, 1500]
而 1200 到 1300 的日志没有备份或归档,那么通常不能无条件地完成连续恢复。恢复会在缺日志的位置停止。
这也是 RMAN 目录、归档备份和日志保留策略必须围绕“可恢复链”设计的原因。
十、备份与 Checkpoint:一致性备份不是“所有文件同一时刻复制”
1. 冷备份
数据库一致关闭后,数据文件、控制文件等处于一致状态,可以进行离线备份。此时仍需根据恢复目标保存相应日志和元数据。
2. 在线备份
在线备份时,数据文件可能在复制过程中继续变化。一个文件甚至可能在复制期间包含不同时间点的块,因此它不是简单的“某一时刻的静态快照”。
Oracle 通过 Redo 使这种备份具备恢复基础:
在线备份得到一个可能不完全一致的文件副本
+ 备份期间及之后所需的 Redo
→ 恢复到一致状态
因此,不能因为某个数据文件复制完成,就认为只要这个文件本身便可恢复。在线备份必须配套保存所需的 Redo。
3. Checkpoint 不等于备份一致性
Checkpoint 可以减少数据库实例恢复的工作量,但它不会:
- 复制数据文件;
- 复制控制文件;
- 保存归档日志;
- 验证备份是否可恢复;
- 为在线备份自动建立完整的恢复链。
RMAN 会结合数据库的 SCN、文件状态和 Redo 需求管理备份与恢复;手工复制文件时尤其不能把 Checkpoint 当成备份工具。
十一、数据流中的并发与顺序约束
1. 多会话同时修改
多个会话可以并发生成 Redo,并写入共享的 Log Buffer。Oracle 必须为 Redo 分配顺序位置,并处理事务提交顺序、块修改顺序和日志写出顺序。
一个事务的提交返回不能只看“它的部分 Redo 是否写出”,而要确保恢复所需的相关日志范围已经满足持久化要求。
2. DBWn 不能违反 WAL
假设某个脏数据块包含 SCN 为 2000 的修改,而对应 Redo 还只在 Log Buffer 中:
数据块:准备写入数据文件
对应 Redo:尚未持久化
DBWn 不能安全地先写这个数据块。否则断电后,数据文件已经包含修改,但 Redo 丢失,恢复无法保证一致性。
因此,数据库写入路径中有一个关键约束:
Data Block Write
必须晚于
对应 Redo 的持久化
这就是 WAL 在 Oracle 中的实际工程含义。
3. 提交顺序与数据块写入顺序可以不同
假设:
事务 T1 先修改块 A,但暂未提交
事务 T2 后修改块 B,并提交
DBWn 先写块 A
LGWR 先写 T2 的提交 Redo
这不是异常。块写入顺序不需要等于事务提交顺序。恢复阶段会根据 Redo 和事务状态重新确定:
T1:没有提交,撤销
T2:有提交,保留
十二、RAC 中的额外边界:Redo 有 thread
在 Oracle RAC 中,每个实例通常有自己的 Redo thread。多个实例并发运行时:
Instance 1 → Thread 1 → Redo sequence 101, 102, ...
Instance 2 → Thread 2 → Redo sequence 205, 206, ...
因此,归档和恢复不能只看单一的 sequence#,还要结合 thread#。
查询日志信息时应至少关注:
SELECT thread#, sequence#, first_change#, next_change#
FROM v$archived_log
ORDER BY thread#, sequence#;
恢复某个 RAC 数据库时,需要覆盖恢复目标所需的各个 thread。只保存其中一个实例的归档日志,可能无法完成整个数据库的连续恢复。
这也是 Data Guard、RMAN 和 RAC 恢复边界不能只用“日志序列号连续”一句话描述的原因:连续性必须在正确的 thread 范围内判断。
十三、Data Guard 中的“传输”和“应用”不是一回事
在 Data Guard 场景中,主库生成 Redo 后,通常还要经历:
主库生成 Redo
→ Redo transport 传输到备库
→ 备库接收并保存
→ Redo apply 应用到备库数据文件
因此存在多个不同位置:
- 主库 LGWR 已写出;
- 主库归档完成;
- 备库已经接收;
- 备库已经应用;
- 备库数据文件已经推进到相应恢复位置。
“备库已经收到日志”不等于“备库已经应用日志”。同步传输、异步传输、保护模式和具体配置会影响提交所等待的远程确认边界,但不改变 Redo、Checkpoint 和事务提交各自的基本概念。
十四、用动态性能视图观察实际状态
以下查询适合在测试库或具有相应权限的环境中执行。
1. 查看归档模式
SELECT name, log_mode, open_mode, database_role
FROM v$database;
关注:
LOG_MODE:ARCHIVELOG或NOARCHIVELOG;OPEN_MODE:数据库当前打开状态;DATABASE_ROLE:主库、物理备库等角色。
2. 查看 Online Redo Log 组
SELECT group#, thread#, sequence#, bytes,
members, archived, status, first_change#
FROM v$log
ORDER BY thread#, group#;
重点理解:
CURRENT:当前正在使用的日志组;ACTIVE:仍可能与恢复有关;INACTIVE:通常可以在满足其他条件后复用;ARCHIVED:反映归档状态,但不能单独替代完整恢复判断。
3. 查看归档目的地状态
SELECT dest_id, status, destination,
error, archived_thread#, archived_seq#
FROM v$archive_dest
WHERE status <> 'INACTIVE'
ORDER BY dest_id;
如果出现归档错误,应先确认:
- 目标路径是否存在;
- 空间是否耗尽;
- 权限是否正确;
- 网络或远程目的地是否可用;
- 是否有归档日志积压。
4. 查看 Checkpoint 相关信息
SELECT checkpoint_change#, current_scn
FROM v$database;
不同版本和视图提供的字段并不完全相同,数据文件层面还可以查询:
SELECT file#, checkpoint_change#, checkpoint_time,
fuzzy, recover
FROM v$datafile_header
ORDER BY file#;
这些字段用于观察数据文件头中的恢复相关状态。不要仅凭一次查询就断言“整个数据库已经安全备份”;需要结合备份工具的元数据、控制文件和 Redo 链验证。
十五、常见失败表现及其因果关系
1. 日志切换频繁,归档跟不上
可能看到:
日志组无法及时复用
日志切换等待增加
归档目的地报错
因果链通常是:
日志生成速度上升
→ Online Redo Log 更快填满
→ 需要更频繁切换
→ ARCn 需要更快归档
→ 归档目的地、I/O 或网络成为瓶颈
→ 日志组不能及时复用
增加日志组数量可以暂时提供缓冲,但如果归档目的地永久不可用,最终仍会耗尽空间。它不是根治手段。
2. ORA-00257 等归档相关错误
典型原因是归档目的地空间不足或无法写入。此时数据库可能无法继续安全地循环使用日志组。
正确排查方向包括:
- 查看
V$ARCHIVE_DEST错误; - 检查文件系统、ASM 磁盘组或远程目标;
- 确认归档日志是否已经按恢复策略备份;
- 使用 RMAN 按策略删除可安全删除的日志;
- 修复归档目的地后验证新的日志切换和归档。
不能直接在操作系统中删除仍可能被恢复或 Data Guard 使用的归档文件。
3. ORA-01555:snapshot too old
这类错误通常与 Undo 保留不足、长查询和高并发修改有关,不是“Redo 不够”。
Redo 主要帮助恢复修改;Undo 主要帮助长查询构造一致读版本。如果长查询需要的旧版本已经被覆盖,就可能出现快照过旧。
这说明:
Redo 完整
≠ Undo 保留足够
4. 恢复时提示需要某个日志序列
恢复过程要求某个归档日志,通常意味着当前数据文件状态与目标恢复位置之间存在 Redo 缺口。此时应核对:
- 日志的
thread#; sequence#;first_change#和next_change#;- 日志文件是否损坏;
- 是否恢复了正确的备份;
- 是否存在另一份归档副本。
不能只按文件名猜测日志顺序。
十六、几个必须避免的等价替换
错误一:COMMIT 就是“写数据文件”
正确理解:
COMMIT 的持久化核心是提交 Redo
数据块由 DBWn 异步或稍后写入数据文件
错误二:Checkpoint 就是提交
正确理解:
Checkpoint 推进数据文件的恢复进度
COMMIT 决定事务是否完成
一个未提交事务可以在 Checkpoint 后出现在数据文件中,恢复时仍会被撤销。
错误三:日志切换就是归档完成
正确理解:
日志切换:切换当前 Online Redo Log group
归档完成:历史日志已经复制到归档目的地
归档可能在切换后继续进行。
错误四:有一份数据文件备份就能恢复
正确理解:
备份提供某个基线状态
Redo 提供从基线到目标状态的变化
两者必须形成可验证的恢复链
错误五:归档日志就是备份
归档日志只保存 Redo,不包含完整数据库数据块。它不能单独替代数据文件备份。反过来,只有数据文件备份而没有所需 Redo,也可能无法恢复到目标时间点。
十七、把一次故障按时间线完整推导
假设数据库发生以下事件:
SCN 1000:完成某次 Checkpoint
SCN 1100:事务 T1 修改块 A,但未提交
SCN 1200:事务 T2 修改块 B,并提交
SCN 1300:DBWn 写出块 A
SCN 1400:数据库异常断电
同时假设:
- T1 的修改 Redo 已写入 Online Redo Log;
- T1 没有提交记录;
- T2 的修改 Redo 和提交记录都已写入 Online Redo Log;
- 块 B 尚未由 DBWn 写入数据文件。
故障时可能是:
数据文件:
块 A:已经包含 T1 的未提交修改
块 B:仍是旧状态
Redo:
包含 T1 的修改
包含 T2 的修改
包含 T2 的提交记录
恢复步骤:
步骤一:从 SCN 1000 开始前滚
恢复读取 SCN 1000 之后的 Redo:
重做 T1 对块 A 的修改
重做 T2 对块 B 的修改
现在两个事务的修改都可能存在于恢复后的数据块中。
步骤二:检查事务状态
T1:没有提交记录
T2:有提交记录
步骤三:回滚 T1
使用 T1 的 Undo 撤销块 A 的修改。
步骤四:保留 T2
块 B 保留 T2 的已提交修改。
最终状态:
T1:回滚
T2:提交并保留
这个算例同时说明:
- Checkpoint 决定恢复大致从哪里开始;
- Redo 使数据块可以前滚;
- 提交 Redo 决定事务是否保留;
- Undo 负责撤销未提交事务;
- DBWn 是否已经写出某个数据块,不决定事务最终是否提交。
十八、生产恢复能力应如何验证
恢复能力不是由某个单独参数保证的,而是由一整条可用链路保证:
可读取的备份
+ 正确的控制文件和元数据
+ 连续且未损坏的 Redo
+ 足够的归档保留
+ 可用的恢复主机和存储
+ 已验证的恢复步骤
在 Oracle 环境中,RMAN 负责记录和执行大量备份恢复工作。实际操作中应区分:
CROSSCHECK:检查备份或归档记录对应的文件是否仍可访问;VALIDATE:验证备份或数据库文件是否可读、结构是否满足检查;RESTORE ... PREVIEW:预览恢复所需的备份;RESTORE:恢复文件;RECOVER:应用 Redo。
这些动作的具体语法和可用选项会受 Oracle 版本、数据库角色、备份介质和权限影响,不能把“备份命令执行成功”直接等同于“已经验证可恢复”。真正可靠的验证应在隔离环境中执行一次完整恢复,至少覆盖目标数据库、所需时间点和关键业务对象。
结语:用四条线理解 Oracle 的恢复语义
Oracle 的写入和恢复路径可以归纳为四条同时推进、但互不等价的线:
- 事务线:事务是否提交,由提交记录和事务状态决定;
- Redo 线:修改是否能够在故障后重做,由 Redo 是否生成并持久化决定;
- 数据文件线:脏块何时落盘,由 DBWn 和 Checkpoint 等写入机制决定;
- 归档线:历史 Redo 能否在 Online Redo 被覆盖后继续使用,由归档和备份保留策略决定。
最重要的恢复逻辑是:
提交成功
→ 提交相关 Redo 已持久化
数据块已落盘
→ 不代表事务已提交
Checkpoint 已完成
→ 不代表已经备份
日志已切换
→ 不代表归档已经完成
归档存在
→ 不代表存在完整的数据文件恢复基线
将这些边界分开之后,LGWR、DBWn、CKPT、ARCn、Online Redo Log、Archived Redo Log、Undo 和 SCN 就不再是互相混淆的名词,而是一条可以从事务执行一直追踪到故障恢复的完整机制链。
系列导航与关联阅读
- 系列入口:数据库完整学习路线:从关系模型、事务索引到分布式与向量检索
- 上一篇:Oracle 存储管理:Tablespace、Segment、Extent、Block 和 ASM
- 下一篇:Oracle 锁与隔离:行锁、ITL、读一致性、死锁和诊断
- 延伸:Oracle 事务与 Undo:读一致性、SCN、锁、Redo 和恢复机制
- 延伸:Oracle RMAN、Data Guard 与 RAC:备份恢复和高可用边界
官方资料
本文依据数据库官方文档重新梳理;正文、示例与生产检查清单由 WR BLOG 编写。

评论
0 条讨论