问题1:ORACLE RAC的DG备库也是RAC,如何修改备库的UNDO默认表空间,直接alter 提示ORA-02097和ORA-01682
备库处于 MOUNT(或 READ ONLY WITH APPLY)状态,数据文件尚未写开,Oracle 怕你把 UNDO 指向一个"目前还不存在或没在线"的表空间,于是抛出
bash
ORA-02097: parameter cannot be modified because specified value is invalid
ORA-01682: unable to extend/allocate the undo segment in the specified undo tablespace
这两个错误说"现在不能改",并非表空间本身有问题。
RAC 环境下每个实例本来就应该使用自己独占的 UNDO 表空间,主库通常已经给每个 THREAD 配好,备库端只要数据文件存在、线程参数正确,不需要也不允许手动去改。
问题2:主库节点1将默认UNDO表空间改为了undotbs3,是不是主库已经同步给备库,备库重启就生效了?
是的,只要主库的修改已经同步到备库的控制文件和 SPFILE,备库重启后就会直接生效,不需要再手动做任何"ALTER SYSTEM"。
或者你也可以在备库修改
alter system set undo_tablespace='UNDOTBS3' sid='abc1' scope=spfile;
具体分两种情况:
主库用 SCOPE=BOTH SID='xxx' 修改了本节点的 UNDO_TABLESPACE
修改会写进 redo,DG 恢复进程会把这条 ALTER SYSTEM 记录一起应用到备库的控制文件和 SPFILE。
备库此时处于 MOUNT 状态,参数虽然已写进 SPFILE,但 内存值并未生效(实例未打开)。
重启备库实例(或整个 RAC 节点)后,实例按 SPFILE 里的新值打开,新的 UNDO 表空间就被启用。
在备库节点只执行了 ALTER SYSTEM SET ... SCOPE=SPFILE SID='xxx'
同样只是把值写进 SPFILE,内存不变;必须重启该实例才能看到新值生效
参考文档
How to set the Undo_tablespace in PDB in Physical Standby RAC Database. (Doc ID 2726173.1)
问题3:如何手动释放UNDOTBS1表空间中的数据?
undo 表空间里的段(undo segment)完全由系统自管;
DBA 不能手工 drop/extent/释放其中的数据。
唯一能把 UNDOTBS1 真正"清空"或"释放"的方法只有:
新建一个 undo 表空间(例如 UNDOTBS3);
把实例切换到新表空间;
-- 在主库节点 1 上:
CREATE UNDO TABLESPACE undotbs3
DATAFILE '+DATA'
SIZE 10G AUTOEXTEND ON NEXT 1G MAXSIZE 31G;
ALTER SYSTEM SET undo_tablespace='UNDOTBS3' SCOPE=BOTH SID='abc1';
等表空间UNDOTBS3显示为空;
sql
SELECT
seg.tablespace_name "Tablespace Name",
ts.bytes/1024/1024 "TS Size(MB)",
ue.status "UNDO Status",
count(*) "Used Extents",
round(sum(ue.bytes)/1024/1024, 2) "Used Size(MB)",
round(sum(ue.bytes)/ts.bytes*100, 2) "Used Rate(%)"
FROM dba_segments seg, DBA_UNDO_EXTENTS ue,
(SELECT tablespace_name, sum(bytes) bytes
FROM dba_data_files GROUP BY tablespace_name) ts
WHERE ue.segment_NAME=seg.segment_NAME and seg.tablespace_name=ts.tablespace_name
GROUP BY seg.tablespace_name, ts.bytes, ue.status ORDER BY seg.tablespace_name;
这种就不能删表空间UNDOTBS3

备库再用UNDOTBS1的时候千万别删除主库的UNDOTB1,否则DG备库会报错的,导致备库不能同步。
备库生效
若主库用了 SCOPE=BOTH,备库 SPFILE 已更新,只需
在备库节点 1
srvctl stop instance -db orcldg -node orcldg1
srvctl start instance -db orcldg -node orcldg1
最后 drop 旧表空间(让 Oracle 自动回收数据文件)
sql
DROP TABLESPACE undotbs1 INCLUDING CONTENTS AND DATAFILES;
Oracle的gv$undostat字段解释
在Oracle数据库中,GV$UNDOSTAT 视图非常重要,它提供了整个数据库集群(RAC环境)中所有实例的UNDO表空间使用情况和事务活动的统计信息,主要服务于自动Undo管理(AUM)。
下面是 GV$UNDOSTAT 核心字段的详细解释,为了帮助你快速了解,我将它们分成了几个类别:
📊 核心字段详解
| 类别 | 字段名 | 说明 |
|---|---|---|
| 基本信息 | INST_ID |
实例编号。在RAC环境中,标识数据来源于哪个实例。 |
BEGIN_TIME |
统计时间段的开始时间。 | |
END_TIME |
统计时间段的结束时间 。默认每10分钟为一个统计间隔。 | |
UNDOTSN |
在此时间段内活动的UNDO表空间的时间戳编号。 | |
| 空间使用 | UNDOBLKS |
在此时间间隔内,事务消耗的UNDO块总数。这是衡量UNDO空间生成速率的核心指标。 |
ACTIVEBLKS |
当前活动事务所占用的UNDO块数(这些事务尚未提交或回滚)。 | |
EXPIREDBLKS |
在此时间间隔内,已过期的UNDO块数 (事务已提交,且保留时间超过UNDO_RETENTION设置)。这些空间可被新事务重用。 |
|
UNEXPIREDBLKS |
在此时间间隔内,未过期的UNDO块数 (事务已提交,但保留时间未超过UNDO_RETENTION)。这些空间应被保留用于一致性读。 |
|
| 事务与查询 | TXNCOUNT |
在时间间隔内执行的事务总数。 |
MAXCONCURRENCY |
在时间间隔内,同时执行的最大并发事务数。 | |
MAXQUERYLEN |
在此时间间隔内,出现的最长查询的执行时间(秒)。这是诊断ORA-01555错误的关键字段。 | |
| 空间压力指标 | EXPSTEALCNT |
为了满足新事务,系统窃取已过期(Expired)的UNDO扩展段的次数。这是正常且期望的行为。 |
UNXPSTEALCNT |
为了满足新事务,系统窃取未过期(Unexpired)的UNDO扩展段的次数 。非零值是一个警告信号,表明UNDO空间可能不足,Oracle正在强制重用本应保留的空间。 | |
SSOLDERRCNT |
在此时间间隔内,发生的 ORA-01555: snapshot too old(快照过旧)错误的数量。任何非零值都意味着需要调整UNDO配置。 |
|
NOSPACEERRCNT |
在此时间间隔内,因无法在UNDO表空间中分配空间而发生的错误数量 。非零值是一个严重警报,表明UNDO表空间严重不足,必须立即处理。 | |
| 优化信息 | TUNED_UNDORETENTION |
Oracle系统自动为您调整的UNDO保留时间(秒)。在AUM模式下,Oracle可能根据负载和最长查询自动调整实际保留时间。 |
💡 主要应用场景
了解这些字段后,你可以利用 GV$UNDOSTAT 视图完成以下几项重要工作:
-
诊断和预防 ORA-01555 错误
这是
GV$UNDOSTAT最经典的应用。你可以通过检查SSOLDERRCNT字段来判断是否发生过此错误,并通过比较MAXQUERYLEN和TUNED_UNDORETENTION来分析错误原因。 -
评估和规划 UNDO 表空间大小
通过观察
UNDOBLKS的历史峰值,并结合UNDO_RETENTION参数,可以估算UNDO表空间的合理大小,避免空间不足或浪费。 -
监控 UNDO 表空间压力
关注
UNXPSTEALCNT(窃取未过期块次数)和NOSPACEERRCNT(空间不足错误计数)等指标。如果这些值非零或持续增长,通常意味着UNDO表空间面临压力,需要及时干预。
一个简单的查询示例
如果你想查看过去一段时间内每个实例是否发生过 ORA-01555 错误,可以执行如下SQL:
sql
SELECT
inst_id,
TO_CHAR(begin_time, 'YYYY-MM-DD HH24:MI') AS begin_time,
TO_CHAR(end_time, 'YYYY-MM-DD HH24:MI') AS end_time,
maxquerylen,
tuned_undoretention,
ssolderrcnt
FROM
gv$undostat
WHERE
ssolderrcnt > 0
ORDER BY
inst_id, begin_time DESC;