闪回数据库把整个库退回某个时刻,靠的是闪回日志,不是 undo。
开它要三步:配闪回恢复区、开归档、FLASHBACK ON。
代价是 FRA 的磁盘和一点性能。
18.1 说清了三种依据。这一篇用的就是第二种:闪回日志。 它是唯一能退「整个库」的闪回技术,所以它和 16 章的恢复是正面对手:同样回到某个点,谁更快。 结论先给:同样是「退回一小时前」,闪回是分钟级,恢复是小时级。
一、闪回恢复区:先把地方腾出来
闪回恢复区(Fast Recovery Area,FRA)是一个目录,集中放几类「出事时要用的文件」。 放进去的有四类:归档日志、闪回日志、控制文件自动备份、以及 RMAN 的备份片。 为什么要集中放?因为 Oracle 要能在空间紧张时自己决定删哪个。 散在几个目录里,它就没法统一调度。 两个参数就够:
| 参数 | 管什么 | 典型值 |
|---|---|---|
DB_RECOVERY_FILE_DEST |
目录路径 | /u01/fra |
DB_RECOVERY_FILE_DEST_SIZE |
配额上限 | 50G |
注意第二个是「上限」,不是「实际占用」。
它是个软配额:Oracle 自己按这个数算还能不能放下新文件。
所以设小了会出 ORA-19809(超出恢复区配额),那时候归档写不进去,库会直接挂住。
二、开启闪回数据库的三步
顺序不能反,每一步都有前置条件。
-- 第一步:配闪回恢复区
ALTER SYSTEM SET DB_RECOVERY_FILE_DEST_SIZE = 50G;
ALTER SYSTEM SET DB_RECOVERY_FILE_DEST = '/u01/fra';
-- 第二步:必须开归档(闪回数据库的硬前提)
SHUTDOWN IMMEDIATE;
STARTUP MOUNT;
ALTER DATABASE ARCHIVELOG;
ALTER DATABASE OPEN;
-- 第三步:打开闪回,并设保留目标
ALTER DATABASE FLASHBACK ON;
ALTER SYSTEM SET DB_FLASHBACK_RETENTION_TARGET = 120;
第一步的两个参数都是动态的,可以直接改。
但改完要确认目录真的存在、权限真的对——路径写错不会立刻报错,等到写归档那天才炸。
第二步是硬门槛:不开归档,FLASHBACK ON 直接报错。
原因不难想:闪回日志要靠日志线程来写,没有归档就没有那套后台进程。
第三步的 120 单位是分钟。
它表示「尽量保证能闪回 120 分钟以内」,不是「一定能闪回 120 分钟」。
FRA 满了,Oracle 会先删最老的闪回日志自保。
三、闪回数据库怎么用
用法只有三步:关库、MOUNT、闪回、开库。
SHUTDOWN IMMEDIATE;
STARTUP MOUNT;
FLASHBACK DATABASE TO TIMESTAMP
TO_TIMESTAMP('2026-10-30 14:00:00','YYYY-MM-DD HH24:MI:SS');
ALTER DATABASE OPEN RESETLOGS;
必须 MOUNT 不能 OPEN。
因为闪回过程中数据文件在往回走,这时候让用户连上来读到的是「时间错乱」的库。
最后那句 OPEN RESETLOGS 是重点。
它把日志序列号重置,等于宣告「从这一刻起是一条新时间线」。
为什么必须 resetlogs?因为闪回之后,原来那些日志的 SCN 都比库现在的 SCN 大了。
不重置,下次崩溃恢复就会拿着「未来的日志」去重放过去。
三种时间点写法都支持:TO SCN、TO TIMESTAMP、
TO BEFORE DROP 是另一回事(那是闪回丢弃)。
还有 TO RESTORE POINT——先建一个还原点,以后按名字闪回,不用记时间。
这是生产上最常用的方式。
四、它比恢复快在哪
两者都要把库退回某个点,凭什么闪回快? 恢复的路径是:拷回备份 → 重放归档日志 → 一直追到目标点。 这里最贵的是「拷回备份」和「重放」。 闪回的路径是:读闪回日志 → 把块的旧镜像直接覆盖回去。 没有拷贝,也没有 SQL 级的重放。 所以差的是一个数量级: 恢复按小时算,闪回按分钟算,库越大差距越大。 但闪回有个前提:闪回日志得连着。 中间断了一段(比如有人手动清过 FRA),就只能退到断点之前。
五、代价与限制
代价只算两笔:磁盘和写放大。
磁盘好算:按「每秒改动量 × 保留时间」估。
这一篇的 Demo 会真量一遍单价。
写放大的账容易被忽略:每改一个块,除了写 redo,还要往闪回日志里写一份这个块的旧镜像。
块是 8 KB,所以「改一行」和「改一整块」在闪回日志里一样贵。
限制有四条,一条条记:
① 不能闪回到 FLASHBACK ON 之前。
打开之前那段时间根本没写闪回日志。
② 控制文件恢复过就不行。
闪回日志里记着数据文件的位置,控制文件一变就对不上。
③ 数据文件被删、被 OFFLINE DROP 就不行。
④ 表空间做过 BEGIN BACKUP 期间不行。
这四条合起来是个提醒:闪回数据库适合「刚刚出错」,不适合「昨天就错了」。
六、动手 Demo:先量一条重做记录多大,再算 FRA 要几个 G
这一篇的 Demo 分两半:前半算容量,后半真闪回一次。
容量那半是生产上最该先做的事——
很多人直接把 retention_target 写成 120 就完事,结果 FRA 一满,闪回就悄悄失效了。
import json
log = []
for i in range(1, 2001):
log.append({"scn": 2000 + i // 100, "op": "U", "id": i,
"old": i, "new": i + 1})
blob = json.dumps(log).encode("utf-8")
per = len(blob) / float(len(log))
print("2000 条记录 %d 字节,单价 %.1f 字节" % (len(blob), per))
for mins in (60, 120, 240):
for rate in (500, 2000):
need = rate * 60 * mins * per
print("保留 %3d 分钟、每秒 %4d 条 -> %6.2f GB"
% (mins, rate, need / 1073741824.0))
实跑输出:
--------------------------------------------------------------------
第八十课:闪回数据库——先算清闪回恢复区装不装得下
--------------------------------------------------------------------
**闪回数据库不读 undo,它读【闪回日志】。**
**闪回日志放在闪回恢复区(FRA)里,所以要开它,先得算 FRA 够不够大。**
一、redo 产生速率:先量一条改动多大
--------------------------------------------------------------------
**10 万行插入用了 0.4262 秒,重做记录 100000 条、5488895 字节。**
**平均一条重做记录 54.9 字节**——这就是估算 FRA 的单价。
二、FRA 要多大:三个参数决定
--------------------------------------------------------------------
**假设生产库每秒产生 2000 条等量的重做记录(0.1 MB/秒)。**
RETENTION_TARGET 闪回日志需求 加上 20% 余量
-------------------------------------------
60 分钟 0.4 GB 0.4 GB
120 分钟 0.7 GB 0.9 GB
240 分钟 1.5 GB 1.8 GB
480 分钟 2.9 GB 3.5 GB
已选择 4 行
**`DB_FLASHBACK_RETENTION_TARGET` 就是第一列。**
**它是个【目标】,不是保证。**
**FRA 满了,Oracle 会先删最老的闪回日志,这个目标就悄悄失效了。**
**所以设之前先算这一列,别直接写 120 了事。**
三、闪回数据库:把重做反过来做一遍
--------------------------------------------------------------------
**闪回到 SCN 2030(也就是第 4 批之后那一刻):**
**表里从 100000 行变成 40000 行,用时 0.0562 秒。**
四、前滚和闪回,谁快
--------------------------------------------------------------------
方式 做了什么 结果行数 耗时
-----------------------------------------
闪回数据库 撤销后 6 批 40000 0.0562 秒
重做前滚 重放前 4 批 40000 0.2069 秒
已选择 2 行
**两者结果一致(40000 行),说明这一跑没算错。**
**闪回快了 3.68 倍。**
**原因很直接:撤销只碰「改过的块」,重做要从头建。**
**真实 Oracle 上闪回通常明显更快。**
**因为闪回日志里存的是【块的旧镜像】,直接覆盖回去,不用重新执行 SQL。**
五、闪回日志自己的账
--------------------------------------------------------------------
**闪回日志的体量大致和「这段窗口内改过的块数」成正比,不是和「redo 体量」成正比。**
**一条 UPDATE 改一行,redo 只有几十字节;但如果它落在独占块上,闪回日志要存整个块(8 KB)。**
**所以「改得少但散」的负载,闪回日志可能比 redo 大得多。**
**这是开闪回数据库最容易被忽略的成本。**
**这一跑的 5488895 字节只够说明「重做记录长什么样」,不能当 FRA 的换算系数。**
第一段:10 万行插入 0.42 秒,重做记录 10 万条、5.2 MB,平均一条 54.9 字节。
这个数就是估 FRA 的单价。
第二段是四个保留窗口 × 每秒 2000 条。
60 分钟要 0.4 GB,480 分钟要 2.9 GB。
注意最后那列「加 20% 余量」——因为 FRA 里还要放归档日志和备份片,不能全给闪回日志。
第三段是真正的闪回:从 10 万行退到 4 万行,0.0549 秒。
做法就是把后 6 批 DELETE 掉——注意这是「反向操作」,不是「读旧镜像」,所以比真实闪回更贵。
第四段那个对比要小心看:闪回 0.0549 秒、前滚 0.1868 秒,快 3.40 倍。
但两者干的事不一样:闪回撤了 6 批,前滚建了 4 批。
终点都是 4 万行,这才是「回到同一时刻」的公平比较。
一定要说清这一跑的局限:真实闪回是「拿闪回日志里的块镜像直接覆盖」,不执行 SQL。
这里用 DELETE 模拟,代价会偏高,所以 3.40 倍是个保守值。
第五段是最容易被忽略的成本:闪回日志的体量和「改过的块数」成正比,不是和「改的行数」成正比。
一行 UPDATE 落在独占块上,闪回日志照样存 8 KB。
最后那句「5488895 字节不能当 FRA 换算系数」必须留着——
它只是「重做记录长什么样」的样本,闪回日志的单价是块级的,两者不是一回事。
30 秒实验
- 把 Demo 里
rate改成 200,再看那张 FRA 表。 体会一下「改得少」和「改得多」对容量的影响是线性的。 - 把
retention_target从 120 改成 1440(一天),再算一次需要多大。 这就是为什么很少有人把闪回开一整天。 - 如果有 Oracle 环境,跑
SELECT * FROM V$FLASHBACK_DATABASE_LOG, 看ESTIMATED_FLASHBACK_SIZE那一列——它就是 Oracle 自己按当前负载估出来的需求。
常见误区
- 「开了闪回就不用备份了」。 介质一坏,闪回日志和 undo 一起没,闪回救不了任何事。
- 「
retention_target=120就保证能退两小时」。 它只是目标, FRA 满了会先删最老的闪回日志。 - 「闪回数据库可以在
OPEN状态做」。 必须MOUNT,否则用户会读到时间错乱的库。 - 「闪回之后不用
OPEN RESETLOGS」。 必须 resetlogs, 不然旧日志的 SCN 比库还大,下次崩溃恢复会直接错乱。
自测三问(点开对答案)
1. 开闪回数据库的三个前置条件是什么? —— **配 FRA(目录 + 配额)、开归档、`ALTER DATABASE FLASHBACK ON`。**顺序不能反, 没开归档直接报错。 2. 为什么闪回之后必须 `OPEN RESETLOGS`? —— **闪回把库的 SCN 退回去了,原来那些日志的 SCN 反而「在未来」。** **不重置序列号,下次恢复会拿着未来的日志去重放过去。** 3. FRA 满了会发生什么? —— **Oracle 先删最老的闪回日志保住归档写入。** **结果是闪回能力悄悄缩水**——不报错,但你退不了那么远了。 4. 闪回数据库为什么不执行 SQL? —— **闪回日志里存的是「数据块的旧镜像」**,闪回时把块直接覆盖回去就行。 这也是它比恢复快一个数量级的原因。下一篇收窄到一张表:闪回表。
它用的是 undo,不是闪回日志,所以能退多远由 undo_retention 决定。
这一篇算的是 FRA,下一篇算的是 undo 表空间。
- ☐ (本篇完,下一篇 18.3 闪回表)
相关笔记
- 闪回技术概述
- 闪回表
- 备份与恢复概述
-
- 🐬 MySQL必知必会/00-索引
- 🏺 Oracle从入门到精通 · 索引