Oracle Undo 表空间与事务管理详解

Oracle Undo 表空间与事务管理详解

适用版本:Oracle Database 19c / 23ai 阅读基础:了解 Oracle 事务、Redo 机制 文档版本:v1.0 / 2026-07


目录


1. 概述:Undo 是事务可回滚的核心保障

Undo 数据是 Oracle 实现事务**原子性(Atomicity)一致性读(Consistent Read)**的核心机制[1][2]。

与 Redo 的区别

维度Redo(重做)Undo(撤销)
作用重放事务,保证持久性回滚事务,保证原子性
记录内容修改后的后映像修改前的前映像
目的故障恢复回滚 + 一致性读
存储位置Redo Log 文件Undo 表空间
写入时机数据修改时数据修改时(同时)

核心特点

  • 存储在 Undo 表空间(自动管理)
  • 循环使用:旧 Undo 数据被新数据覆盖
  • 被多个机制使用:回滚、一致性读、闪回
  • 必须配合 Redo:每次修改同时生成 Redo 和 Undo

2. Undo 的核心作用

Undo 数据有三大用途[1]:

2.1 事务回滚

-- 用户执行
UPDATE emp SET sal = sal * 1.1 WHERE deptno = 10;
-- 5 行更新

ROLLBACK;
-- Oracle 用 Undo 数据把 sal 改回原值

2.2 一致性读(Consistent Read)

-- 用户 A 9:00 开始查询
SELECT AVG(sal) FROM emp;
-- 查询耗时 10 分钟

-- 期间用户 B 9:05 更新了 sal 并提交
UPDATE emp SET sal = sal * 1.1 WHERE empno = 7900;
COMMIT;

-- 用户 A 9:10 查询完成
-- Oracle 用 Undo 数据构造 9:00 时刻的数据快照
-- 不受用户 B 修改影响

2.3 闪回查询

-- 查询 1 小时前的数据
SELECT * FROM emp 
AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '1' HOUR);

-- 闪回表
FLASHBACK TABLE emp TO TIMESTAMP (SYSTIMESTAMP - INTERVAL '1' HOUR);

其他用途

  • 闪回事务查询(FLASHBACK_TRANSACTION_QUERY
  • 闪回版本查询(VERSIONS BETWEEN
  • 逻辑备用库事务应用

3. Undo 管理方式演进

3.1 手动 Undo 管理(Rollback Segment)

Oracle 9i 之前采用手动管理[1]:

特点

  • DBA 手工创建 Rollback Segment
  • 需调整大小、数量
  • 容易出现 ORA-01555 错误
  • 管理复杂,已废弃

参数

ROLLBACK_SEGMENTS = (rbs01, rbs02, ...)

3.2 自动 Undo 管理(AUM)

Oracle 9i 起引入 AUM(Automatic Undo Management)[1]:

特点

  • 数据库自动管理 Undo Segment
  • DBA 只需配置 Undo 表空间大小和保留时间
  • 自动扩展和回收
  • 大幅减少 ORA-01555

参数

UNDO_MANAGEMENT = AUTO          -- 自动管理(默认)
UNDO_TABLESPACE = UNDOTBS1      -- Undo 表空间
UNDO_RETENTION = 900            -- 保留时间(秒)

坑 1:Oracle 11g 起默认就是 AUM,不要再配置 ROLLBACK_SEGMENTS 参数。


4. Undo 表空间架构

4.1 Undo Segment 与 Undo Block

Undo 表空间
  ├── Undo Segment 1
  │     ├── Extent 1
  │     │     ├── Undo Block 1 (事务 A 的前映像)
  │     │     ├── Undo Block 2 (事务 B 的前映像)
  │     │     └── Undo Block N
  │     └── Extent 2
  │           └── ...
  ├── Undo Segment 2
  │     └── ...
  └── Undo Segment N

Undo Segment

  • 自动创建和维护
  • 每个事务绑定到一个 Undo Segment
  • 一个 Undo Segment 可服务多个事务

Undo Block

  • 存储具体的前映像数据
  • 包含事务 ID、SCN、数据块地址、前映像

4.2 Undo Block 内容

Undo Block 结构:
  ┌──────────────────────────────────┐
  │ Undo Block Header                │
  │  - 事务表(Transaction Table)    │
  │  - 事务槽(Tx Slot)              │
  ├──────────────────────────────────┤
  │ Undo Record 1                    │
  │  - 数据块地址 (file#, block#)     │
  │  - 偏移量                         │
  │  - 前映像(Before Image)         │
  │  - 操作类型(INSERT/UPDATE/DELETE)│
  │  - SCN                            │
  ├──────────────────────────────────┤
  │ Undo Record 2                    │
  │  ...                             │
  └──────────────────────────────────┘

4.3 事务槽(Transaction Slot)

每个 Undo Block 头部有事务表,记录活跃事务[2]:

-- 查看数据块中的事务槽
SELECT * FROM v$transaction;

关键字段

字段含义
XIDUSNUndo Segment 号
XIDSLOT事务槽号
XIDSQN序列号
UBAFILUndo Block 文件号
UBABLKUndo Block 块号
STATUS事务状态(ACTIVE/COMMITTED)
START_TIME事务开始时间

5. Undo 与事务流程

5.1 事务开始

1. 用户发出 DML(INSERT/UPDATE/DELETE)
2. Oracle 在 Undo Segment 中分配事务槽
3. 在数据块头记录事务槽位置
4. 事务开始

5.2 数据修改

UPDATE emp SET sal = 5000 WHERE empno = 7900;

执行过程:
1. 在 Buffer Cache 找到数据块(file=4, block=1234)
2. 在 Undo Segment 找一个 Undo Block
3. 把 sal 的原值(如 800)写入 Undo Block
4. 修改数据块中的 sal = 5000
5. 同时生成 Redo Record(记录 Undo 和 Data 变化)
6. 数据块和 Undo Block 都变脏

5.3 事务提交

COMMIT;

执行过程:
1. 在 Undo Block 头标记事务为 COMMITTED
2. 生成 Redo Record 记录提交
3. LGWR 把 Redo 写入 Redo Log
4. 释放锁
5. 返回成功

坑 2:提交后 Undo 数据不会立即删除,需要保留到 undo_retention 时间到期,供一致性读使用。

5.4 事务回滚

ROLLBACK;

执行过程:
1. 读取 Undo Block 中的前映像
2. 把数据块恢复到原值
3. 在 Undo Block 标记事务为 ROLLED BACK
4. 释放锁

6. 一致性读(Consistent Read)

Oracle 实现 MVCC(多版本并发控制),每个查询看到的是查询开始时刻的快照[1]。

实现原理

查询开始时刻 SCN = 1000
查询过程中遇到数据块:
  ├── 数据块当前 SCN < 1000
  │   → 直接读取(数据未变化)
  └── 数据块当前 SCN > 1000
      → 数据已被修改
      → 从 Undo Block 构造前映像(SCN=1000 时刻的数据)

示例

-- 会话 A
SELECT * FROM emp WHERE empno = 7900;
-- sal = 800

-- 会话 B
UPDATE emp SET sal = 5000 WHERE empno = 7900;
COMMIT;

-- 会话 A 再次查询(新事务)
SELECT * FROM emp WHERE empno = 7900;
-- sal = 5000(已读最新值)

-- 会话 A 旧事务继续(一致性读)
-- 仍看到 sal = 800(从 Undo 构造)

7. ORA-01555 错误详解

7.1 错误原因

ORA-01555(snapshot too old)是最常见的 Undo 相关错误[3][4]:

ORA-01555: snapshot too old: rollback segment number  with name "..." too small
Cause: rollback records needed by a reader for consistent read are overwritten by other writers
Action: If in Automatic Undo Management mode, increase undo_retention setting. Otherwise, use larger rollback segments

根本原因[3]:查询需要的 Undo 数据已被覆盖。

7.2 触发场景

时间线:
T1: 长查询开始(需要 T1 时刻的快照)
T2: 其他事务修改数据并提交(生成 Undo 数据,标记为 UNEXPIRED)
T3: Undo 表空间不足,新事务覆盖 T2 的 Undo 数据(标记为 EXPIRED)
T4: 长查询访问被修改的数据
    → 试图从 Undo 构造 T1 时刻的快照
    → Undo 已被覆盖
    → ORA-01555

关键判断[3]:

  • 不是”配置错了”,而是 Undo 空间/保留时间不足
  • undo_retention 是”建议保留时长”,是否能满足取决于空间
  • 表空间 AUTOEXTEND ON 时,Oracle 会优先扩展而非覆盖
  • 表空间固定大小或已到 MAXSIZE,再高的 undo_retention 也无效

7.3 解决方案

步骤 1:诊断[3]

-- 查当前 Undo 配置
SHOW PARAMETER undo

-- 查看 Undo 表空间大小
SELECT tablespace_name, 
       SUM(bytes) / 1024 / 1024 / 1024 AS size_gb
FROM dba_data_files
WHERE tablespace_name LIKE 'UNDOTBS%'
GROUP BY tablespace_name;

-- 查历史最长查询耗时
SELECT MAX(maxquerylen) AS max_seconds 
FROM v$undostat;
-- 这是 undo_retention 的硬下限

-- 查 Undo 使用率
SELECT TO_CHAR(begin_time, 'HH24:MI'), 
       (undotsn / MAXCONCURRENCY) * 100 AS pct_used
FROM v$undostat
ORDER BY begin_time DESC
FETCH FIRST 10 ROWS ONLY;

步骤 2:扩容 Undo 表空间[3]

-- 推荐方式:添加新数据文件
ALTER TABLESPACE undotbs1 
ADD DATAFILE '/u01/oradata/orcl/undotbs02.dbf' 
SIZE 2G 
AUTOEXTEND ON NEXT 512M MAXSIZE 24G;

步骤 3:调整 undo_retention[3]

-- 提高 undo_retention(如长查询需要 1 小时)
ALTER SYSTEM SET undo_retention = 3600 SCOPE=BOTH;

-- 注意:必须配合扩容表空间,否则无效

步骤 4:切换到新 Undo 表空间(如已满)[3]

-- 创建新的 Undo 表空间
CREATE UNDO TABLESPACE undotbs2 
DATAFILE '/u01/oradata/orcl/undotbs2_01.dbf' SIZE 4G
AUTOEXTEND ON NEXT 512M MAXSIZE 32G;

-- 切换
ALTER SYSTEM SET undo_tablespace = undotbs2 SCOPE=BOTH;

-- 旧表空间的所有事务结束后才能删除

7.4 预防措施

-- 1. 启用 Undo Guarantee(强制保留)
ALTER TABLESPACE undotbs1 RETENTION GUARANTEE;

-- 2. 应用层:避免长查询与大量 DML 并发
-- 3. 应用层:批量更新分批 commit,避免一次性大事务
-- 4. 监控 v$undostat

8. Undo 相关参数

参数含义默认值
undo_managementUndo 管理方式(AUTO/MANUAL)AUTO
undo_tablespaceUndo 表空间名第一个 Undo 表空间
undo_retentionUndo 保留时间(秒)900(15 分钟)
temp_undo_enabled临时 Undo(12c+)FALSE

查看参数

SHOW PARAMETER undo

调整参数

-- 在线调整 undo_retention
ALTER SYSTEM SET undo_retention = 1800 SCOPE=BOTH;

-- 修改 undo_tablespace(需重启或新事务生效)
ALTER SYSTEM SET undo_tablespace = undotbs2 SCOPE=BOTH;

9. Undo 相关视图

9.1 v$undostat

-- Undo 使用统计(10 分钟一桶)
SELECT 
    TO_CHAR(begin_time, 'YYYY-MM-DD HH24:MI') AS begin_time,
    TO_CHAR(end_time, 'YYYY-MM-DD HH24:MI') AS end_time,
    activeblks,
    unexpledblk,
    expblkreu,
    maxquerylen,
    maxconcurrency,
    undotsn
FROM v$undostat
ORDER BY begin_time DESC
FETCH FIRST 10 ROWS ONLY;

关键字段

字段含义
ACTIVEBLKS活跃事务占用的块数
UNEXPLEDBLK未过期但已被覆盖的块数(应接近 0)
EXPBLKREU过期块重用数
MAXQUERYLEN最大查询时长(秒)
MAXCONCURRENCY最大并发事务数
UNDOTSNUndo 表空间号

9.2 dba_undo_extents

-- 查看 Undo Extent 状态
SELECT tablespace_name, status, COUNT(*) AS extents,
       SUM(bytes) / 1024 / 1024 AS size_mb
FROM dba_undo_extents
GROUP BY tablespace_name, status
ORDER BY tablespace_name, status;

Status 取值

状态含义
ACTIVE当前事务使用中
UNEXPIRED已提交但未过 undo_retention
EXPIRED已过 undo_retention,可重用

9.3 v$transaction

-- 当前活跃事务
SELECT addr, xidusn, xidslot, xidsqn, 
       status, start_time, used_ublk, used_urec
FROM v$transaction
ORDER BY start_time;

9.4 v$rollstat

-- Undo Segment 统计
SELECT usn, name, xacts, waits, 
       hwmsize, shrinks, wraps, extends
FROM v$rollstat r, v$rollname n
WHERE r.usn = n.usn;

9.5 dba_rollback_segs

SELECT segment_name, tablespace_name, status, 
       instance_num, next_extent, max_extents
FROM dba_rollback_segs
WHERE tablespace_name LIKE 'UNDOTBS%';

10. Undo 表空间管理

10.1 创建 Undo 表空间

-- 创建
CREATE UNDO TABLESPACE undotbs1
DATAFILE '/u01/oradata/orcl/undotbs01.dbf'
SIZE 1G
AUTOEXTEND ON NEXT 100M MAXSIZE 10G
EXTENT MANAGEMENT LOCAL;

-- 指定为 Undo 表空间
ALTER SYSTEM SET undo_tablespace = undotbs1 SCOPE=SPFILE;

10.2 切换 Undo 表空间

-- 创建新表空间
CREATE UNDO TABLESPACE undotbs2 
DATAFILE '/u01/oradata/orcl/undotbs2_01.dbf' SIZE 2G
AUTOEXTEND ON NEXT 100M MAXSIZE 20G;

-- 在线切换
ALTER SYSTEM SET undo_tablespace = undotbs2 SCOPE=BOTH;

-- 旧表空间状态变为 PENDING OFFLINE
-- 待所有旧事务结束后变为 AVAILABLE

坑 3:切换后旧表空间不能立即删除,需要等所有活跃事务结束。

10.3 删除 Undo 表空间

-- 查看状态
SELECT tablespace_name, status FROM dba_tablespaces
WHERE contents = 'UNDO';

-- 必须先切换到其他 Undo 表空间
ALTER SYSTEM SET undo_tablespace = undotbs1 SCOPE=BOTH;

-- 确认旧表空间无活跃事务
SELECT COUNT(*) FROM v$transaction WHERE xidusn = (SELECT usn FROM v$rollname WHERE name LIKE 'SYSSMU%undotbs2%');

-- 删除
DROP TABLESPACE undotbs2 INCLUDING CONTENTS AND DATAFILES;

10.4 添加数据文件

ALTER TABLESPACE undotbs1 
ADD DATAFILE '/u01/oradata/orcl/undotbs02.dbf' 
SIZE 2G 
AUTOEXTEND ON NEXT 100M MAXSIZE 10G;

10.5 Undo 表空间 resize

-- 查看数据文件
SELECT file_name, bytes/1024/1024 AS size_mb, 
       autoextensible, maxbytes/1024/1024 AS max_mb
FROM dba_data_files
WHERE tablespace_name = 'UNDOTBS1';

-- resize
ALTER DATABASE DATAFILE '/u01/oradata/orcl/undotbs01.dbf' 
RESIZE 5G;

-- 启用自动扩展
ALTER DATABASE DATAFILE '/u01/oradata/orcl/undotbs01.dbf'
AUTOEXTEND ON NEXT 100M MAXSIZE 10G;

11. Undo 调优

11.1 估算 Undo 空间需求

-- 查看历史最大 Undo 消耗
SELECT 
    MAX(undoblks) / 600 AS blocks_per_sec,
    MAX(undoblks) * 8192 / 1024 / 1024 AS max_undo_gb_per_10min
FROM v$undostat;

-- 估算公式:
-- Undo 大小 = (每秒 Undo 块数 × undo_retention × 块大小)

11.2 调优 undo_retention

-- 查看实际保留时间(TUNED_UNDORETENTION)
SELECT 
    TO_CHAR(begin_time, 'HH24:MI') AS time,
    tuned_undoretention AS actual_retention_sec,
    maxquerylen AS max_query_sec
FROM v$undostat
ORDER BY begin_time DESC
FETCH FIRST 10 ROWS ONLY;

-- 应确保 tuned_undoretention > maxquerylen

11.3 启用 Undo Guarantee

-- 查看当前状态
SELECT tablespace_name, retention FROM dba_tablespaces
WHERE contents = 'UNDO';

-- 启用 Guarantee(强制保留 Undo 到 undo_retention)
ALTER TABLESPACE undotbs1 RETENTION GUARANTEE;

-- 关闭
ALTER TABLESPACE undotbs1 RETENTION NOGUARANTEE;

坑 4:启用 Guarantee 后,如果 Undo 表空间不足,新事务会失败(而不是覆盖旧 Undo)。需要确保空间充足。


12. 常见坑与排错

坑 1:ORA-01555 snapshot too old

见第 7 节详细解决方案。

坑 2:Undo 表空间满

现象:DML 失败,报 ORA-30036。

解决

-- 1. 查看使用情况
SELECT tablespace_name, 
       SUM(bytes)/1024/1024 AS total_mb,
       SUM(CASE WHEN autoextensible='YES' THEN maxbytes ELSE bytes END)/1024/1024 AS max_mb
FROM dba_data_files
WHERE tablespace_name = 'UNDOTBS1'
GROUP BY tablespace_name;

-- 2. 添加数据文件
ALTER TABLESPACE undotbs1 ADD DATAFILE '...' SIZE 2G AUTOEXTEND ON;

-- 3. 查看大事务
SELECT sid, serial#, used_ublk, used_urec, start_time
FROM v$transaction t, v$session s
WHERE t.ses_addr = s.saddr
ORDER BY used_ublk DESC;

-- 4. kill 大事务(如必要)
ALTER SYSTEM KILL SESSION 'sid,serial#';

坑 3:长事务持有 Undo 不释放

现象:Undo 使用率持续高。

诊断

-- 查看长事务
SELECT sid, serial#, username, 
       used_ublk, used_urec,
       TO_CHAR(start_time, 'YYYY-MM-DD HH24:MI:SS') AS start_time,
       module, machine
FROM v$transaction t, v$session s
WHERE t.ses_addr = s.saddr
  AND start_time < SYSDATE - 1/24
ORDER BY start_time;

解决

  1. 联系应用团队确认是否正常
  2. 必要时 kill session
  3. 优化应用,避免长事务

坑 4:LOB 列引发 ORA-01555

现象:表有 LOB 列,查询报 ORA-01555。

原因:LOB 列的 Undo 不存在 Undo 表空间,而是存在 LOB 段本身,由 PCTVERSIONRETENTION 控制。

解决

-- 查看 LOB 配置
SELECT table_name, column_name, segment_name, 
       pctversion, retention
FROM dba_lobs
WHERE table_name = 'YOUR_TABLE';

-- 调整 PCTVERSION
ALTER TABLE your_table MODIFY LOB (lob_column) (PCTVERSION 20);

-- 或改为 RETENTION(需表空间为 ASSM)
ALTER TABLE your_table MODIFY LOB (lob_column) (RETENTION);

坑 5:Undo 表空间过大无法缩小

现象:Undo 表空间 resize 失败。

解决

-- 1. 切换到新 Undo 表空间
CREATE UNDO TABLESPACE undotbs_new DATAFILE '...' SIZE 2G;
ALTER SYSTEM SET undo_tablespace = undotbs_new SCOPE=BOTH;

-- 2. 等待旧表空间所有事务结束
SELECT COUNT(*) FROM v$transaction WHERE xidusn IN (
    SELECT usn FROM v$rollname WHERE name IN (
        SELECT segment_name FROM dba_segments WHERE tablespace_name = 'UNDOTBS1'
    )
);

-- 3. 删除旧表空间
DROP TABLESPACE undotbs1 INCLUDING CONTENTS AND DATAFILES;

坑 6:闪回查询失败

现象FLASHBACK TABLEAS OF TIMESTAMP 查询失败。

解决

-- 1. 增大 undo_retention(闪回需更长保留)
ALTER SYSTEM SET undo_retention = 86400 SCOPE=BOTH;  -- 24 小时

-- 2. 扩容 Undo 表空间

-- 3. 启用 Undo Guarantee
ALTER TABLESPACE undotbs1 RETENTION GUARANTEE;

13. 最佳实践

  1. 必须用 AUM:所有 11g+ 数据库默认[1]
  2. Undo 表空间 AUTOEXTEND ON:避免 ORA-01555
  3. 合理设置 undo_retention:参考 v$undostat.maxquerylen[3]
  4. 核心系统启用 Guarantee:强制保留 Undo
  5. 监控 v$undostat:发现异常及时处理
  6. 监控 Undo 使用率:阈值 80%
  7. 应用层避免长事务:分批 commit
  8. 批量操作分批提交:避免大事务占满 Undo
  9. LOB 列单独配置 PCTVERSION/RETENTION:避免 ORA-01555
  10. 闪回场景至少保留 24 小时undo_retention = 86400
  11. OLTP 通常 15-30 分钟undo_retention = 900-1800
  12. ETL 临时调高:批量时段 undo_retention = 3600+
  13. Undo 表空间与数据文件分开:避免 I/O 争用
  14. 定期清理旧 Undo 表空间:避免空间浪费

14. 参考资料

[1] 墨天轮,暮雨,《Oracle 体系结构-日志文件汇总》: https://www.modb.pro/db/1970883931515924480

[2] Oracle Database 19c Administrator’s Guide,Managing Undo: https://docs.oracle.com/en/database/oracle/oracle-database/19/admin/managing-undo.html

[3] php.cn,《如何配置 Oracle 的快照过旧(Snapshot Too Old)错误的 Undo 空间》: https://m.php.cn/faq/2846110.html

[4] Oracle MOS Note 1555.1,ORA-01555 错误说明: https://support.oracle.com/knowledge/Oracle%20Database%20Products/1555_1.html

[5] Oracle MOS Note 1580790.1,Troubleshooting ORA-01555: https://support.oracle.com/knowledge/Oracle%20Cloud/1580790_1.html

[6] Oracle Database 19c Concepts,Undo: https://docs.oracle.com/en/database/oracle/oracle-database/19/cncpt/undo.html

[7] CSDN,《深入浅出 Oracle:DBA 入门、进阶与诊断案例》读书笔记: https://blog.csdn.net/weixin_33910460/article/details/94703916


相关文章