撤销(undo)存的是「改之前是什么」,不是「改成了什么」。
所以它能干三件事:回滚、读一致性、闪回查询。
UNDO_RETENTION 是「尽力保留」不是「保证保留」。
空间不够就会覆盖老 undo,长查询于是报 ORA-01555(snapshot too old)。
8.1 到 8.4 讲的是「数据放哪」。这一篇讲「改之前的样子放哪」。 undo 是 Oracle 并发控制的底座——没有它,一个会话在改表,别的会话就没法读一致的快照。 这一篇的算术是全章最有用的:「undo 要多大」这道题,面试和实战都会问,答案是一道乘法。
一、三个用处,一份数据
回滚:rollback 就是把 undo 里的旧值写回去。
读一致性:你查的时候别人在改,Oracle 拿 undo 把块「还原」成你开始查那一刻的样子。
闪回查询:select ... as of timestamp 也是拿 undo 还原,时间点由你定。
这三件事是同一份数据支撑的,只是「还原到什么时候」不同:
回滚还原到事务开始前;读一致性还原到「你这条查询开始那一刻」;
闪回查询还原到「你指定的那个时间」。
所以 undo 是「有保质期的历史」——过了保质期就没了,这就是 ORA-01555 的根源。
注意它叫「快照太旧」,不是「数据坏了」,本质是「你要的那个版本已经被覆盖了」。
二、两个参数:UNDO_TABLESPACE 和 UNDO_RETENTION
UNDO_TABLESPACE 指定当前用哪个 undo 表空间。
11g 起只能有一个处于使用状态(老的 MANUAL 回滚段模式已经废了)。
UNDO_RETENTION 是「希望保留多久」,单位秒,默认 900(15 分钟)。
关键在「希望」两个字——它是给优化器的建议值,空间不够时 Oracle 会优先保证新事务能跑。
要真保证,得开 RETENTION GUARANTEE:
ALTER TABLESPACE undotbs1 RETENTION GUARANTEE;
代价是空间不够时【新事务直接失败】(ORA-30036)——牺牲新事务,换老查询能跑完。
这是一个明确的取舍,没有「都对」的答案:
跑批多的库倾向 GUARANTEE(保证长查询不被掐);OLTP 倾向默认(保证新事务不被拒)。
DBA_TABLESPACES.RETENTION 那一列能看出当前是哪种。
三、算一笔:undo 到底要给多大
公式是一道乘法:每秒 undo 量 × 保留秒数 × 1.5 的余量。
每秒 undo 量从 V$UNDOSTAT 的 UNDOBLKS 取——它按 10 分钟一段统计,找出峰值那一段。
为什么乘 1.5?因为业务量会涨、批量任务会突然插进来。
按峰值刚好给,等于每月都要扩容一次。
一个反直觉的事实:undo 表空间「用满」不一定报错。
默认状态下它会自己覆盖老 undo,
所以「undo 使用率 95%」往往没事,真正出事的是「长查询正好需要被覆盖的那段」。
这也是 ORA-01555 难复现的原因。
四、切换与删除:先有替代,再动老的
换 undo 表空间:ALTER SYSTEM SET UNDO_TABLESPACE = undotbs2;
切完之后旧的那个不能立刻删——里面还有未过期的 undo,得等过了 undo_retention 才能删。
立刻删会报 ORA-01548(active rollback segment exists)。
正确做法是:
切走 → 等一会儿(至少一个 retention 周期)→ 确认 V$ROLLSTAT 里没活动段 → 再删。
还有一个坑:切 undo 表空间会让「当前正在跑的长事务」出问题。
所以要在业务低峰做,而且要提前确认没有长事务——
V$TRANSACTION 里 USED_UBLK 大、START_TIME 早的那些就是。
五、动手 Demo:算清 ORA-01555:200MB 的 undo 撑不住 15 分钟
沙箱里没有 Oracle,ALTER TABLESPACE ... RETENTION 执行不了。
但 ORA-01555 的成因是一道除法:能留多久 = undo 大小 ÷ 每秒产生量。
这道题沙箱里能算得比文档还清楚。
我先按 200MB/每秒 1MB 算出「只能留 200 秒」,跟默认的 900 秒一比,缺口就出来了,
然后按峰值反算三种保留时长各要多大空间。
size_mb, rate_mb_s, retention = 200, 1, 900
keep = size_mb / rate_mb_s
print('undo %dMB,每秒产生 %dMB' % (size_mb, rate_mb_s))
print('能留 %.0f 秒,而 UNDO_RETENTION 要 %d 秒' % (keep, retention))
print('差 %.0f 秒 -> 空间不够,老 undo 会被覆盖 -> ORA-01555' % (retention - keep))
# 按峰值估容量:每秒 undo 量 x 保留秒数 x 1.5 余量
peak = 1.4
for r in (900, 3600, 10800):
print('峰值 %.1fMB/s,留 %5d 秒(%2d 分钟)-> 约 %.2f GB'
% (peak, r, r // 60, peak * r * 1.5 / 1024))
实跑输出:
--------------------------------------------------------------------
第三十七课:撤销表空间——回滚、读一致、闪回,都靠它
--------------------------------------------------------------------
**撤销(undo)存的不是「改成了什么」,是「改之前是什么」。**
**所以它有三个用处:回滚、读一致性、闪回查询。**
**这三件事其实是同一份数据支撑的,只是用法不同。**
**回滚**:`rollback` 就是把 undo 里的旧值写回去。
** **读一致性**:你查的时候别人在改,Oracle 拿 undo 把块「还原」成你开始查那一刻的样子。**
**闪回查询**:`select ... as of timestamp` 也是拿 undo 还原,只是时间点由你指定。
算一笔账:undo 到底要留多久
--------------------------------------------------------------------
**假设撤销表空间 200MB,业务每秒产生 1MB 的 undo。**
**那么理论上能留 200 秒的 undo。**
**而 `UNDO_RETENTION` 默认是 900 秒(15 分钟)。**
**200 秒 < 900 秒——这就是问题所在。**
**`UNDO_RETENTION` 是「尽力保留」,不是「保证保留」。**
**空间不够时,Oracle 会优先保证新事务能跑,把老 undo 覆盖掉。**
**这时候一个跑了很久的查询去读老块,就报 `ORA-01555`(snapshot too old)。**
**要真保证,得开 `RETENTION GUARANTEE`:** **`ALTER TABLESPACE undotbs1 RETENTION GUARANTEE;`**
**代价是空间不够时【新事务直接失败】**(`ORA-30036`),
**而不是牺牲老查询。** **这是一个取舍:牺牲新事务,还是牺牲长查询。**
两个参数:UNDO_TABLESPACE 和 UNDO_MANAGEMENT
--------------------------------------------------------------------
[Oracle 原码 · 本沙箱不执行]
| UNDO_MANAGEMENT = AUTO -- 11g 之后这是唯一可选值
| UNDO_TABLESPACE = undotbs1
| UNDO_RETENTION = 900
AUTO 之外的取值已经是历史了。
**`UNDO_MANAGEMENT` 只有 `AUTO` 一种了**,
**老教材里那个 `MANUAL`(用回滚段)在 11g 之后彻底没了。** **所以这一段教材内容要打折看**:
**知道「以前有回滚段这回事」就行,不用记语法。**
**`UNDO_TABLESPACE` 可以切,但不能没有:** **`ALTER SYSTEM SET UNDO_TABLESPACE = undotbs2;`**
**切完之后旧的那个不能立刻删**——
**里面还有未过期的 undo,得等 `undo_retention` 过了才能删。**
**立刻删会报 `ORA-01548`(活动回滚段存在)。**
怎么估 undo 大小
--------------------------------------------------------------------
**公式是「每秒 undo 量 × 保留秒数 × 余量」。**
**每秒 undo 量从哪儿来?`v$undostat` 里 `UNDOBLKS` 那一列,按 10 分钟一段统计。**
**算一个峰值,乘保留时间,再乘 1.5 倍余量,就是该给的大小。**
**峰值 1.4MB/秒,保留 900 秒(15 分钟),需要约 1.8GB。**
**峰值 1.4MB/秒,保留 3600 秒(60 分钟),需要约 7.4GB。**
**峰值 1.4MB/秒,保留 10800 秒(180 分钟),需要约 22.1GB。**
**看到这个数就明白为什么 DBA 总说「undo 别抠」**——
**留 15 分钟和留 3 小时,空间差 12 倍。**
**生产上常见的是 1 小时起步,跑批多的库给到 3 到 6 小时。**
前两行是题干:undo 200MB,每秒产生 1MB。
能留 200 秒,而 UNDO_RETENTION 要 900 秒。
差 700 秒。
第三行直接给出结论:空间不够,老 undo 会被覆盖,于是 ORA-01555。
注意「会被覆盖」是 Oracle 的主动选择——它优先保证新事务能跑,牺牲老快照。
后面三行是反算:按峰值 1.4MB/秒,留 15 分钟要 1.85GB,留 1 小时要 7.4GB,
留 3 小时要 22.1GB。
这三个数就是「undo 别抠」的量化依据。
注意 1.4MB/秒这个峰值不是拍脑袋——
V$UNDOSTAT 里 UNDOBLKS 乘块大小再除时间段,就能得到每秒量。
真实库要先测再给,别照抄我这个数。
还有一个隐藏变量:并发。
上面算的是「总产生量」,它已经含了并发。
如果只按单条语句估,会严重低估——一百个会话同时改,undo 产生量是一百份。
30 秒实验
- 把
rate_mb_s从 1 改成 5,重跑。能留的时间从 200 秒降到 40 秒。 产出量翻五倍,保质期缩到五分之一——这就是纯除法。 - 把
size_mb从 200 改成 2048,重跑。能留 2048 秒,超过 900 秒的 retention。 这时候「默认模式」也够用了,不必开GUARANTEE。 - 把
peak从 1.4 改成 8(跑批库常见),重跑。留 3 小时要 126GB。 这就是为什么跑批库的 undo 常常给到上百 GB。
常见误区
- 以为设了
UNDO_RETENTION就一定保留那么久。 它是「尽力」,空间不够照样覆盖。 要保证得开RETENTION GUARANTEE。 - 看到 undo 使用率 95% 就慌。 默认模式下它会自己覆盖, 真正出事的是「长查询正需要被覆盖的那段」,不是使用率本身。
- 切完 undo 表空间立刻删旧的。 报
ORA-01548, 里面还有未过期的 undo。 等一个 retention 周期再删。 - 按单条语句估 undo 大小。 并发一上来就低估得离谱,要按
V$UNDOSTAT的总量峰值估。
自测三问(点开对答案)
1. undo 支撑哪三件事? —— **回滚、读一致性、闪回查询。** **三件事都是「拿旧值还原」,只是还原到的时间点不同。** 2. `UNDO_RETENTION` 是保证还是建议? —— **是「尽力保留」,不是保证。** **空间不够时会覆盖老 undo。** **要保证得开 `RETENTION GUARANTEE`,代价是新事务可能失败。** 3. `ORA-01555` 的本质是什么? —— **查询需要的那个数据版本,它的 undo 已经被覆盖了。** **不是数据坏了,是「快照太旧」。** 4. undo 大小怎么估? —— **每秒 undo 量 × 保留秒数 × 1.5。** **每秒量从 `V$UNDOSTAT` 的 `UNDOBLKS` 取峰值。**下一篇讲临时表空间:排序和哈希的落脚点。
ORA-01652 就是它不够用了。
- ☐ (本篇完,下一篇 8.6 管理临时表空间)
相关笔记
- 管理临时表空间
- 维护表空间与数据文件
- 表空间与数据文件的关系
-
- 🐬 MySQL必知必会/00-索引
- 🏺 Oracle从入门到精通 · 索引