Oracle Undo 表空间与事务管理详解
Oracle Undo 表空间与事务管理详解
适用版本:Oracle Database 19c / 23ai 阅读基础:了解 Oracle 事务、Redo 机制 文档版本:v1.0 / 2026-07
目录
- 1. 概述:Undo 是事务可回滚的核心保障
- 2. Undo 的核心作用
- 3. Undo 管理方式演进
- 4. Undo 表空间架构
- 5. Undo 与事务流程
- 6. 一致性读(Consistent Read)
- 7. ORA-01555 错误详解
- 8. Undo 相关参数
- 9. Undo 相关视图
- 10. Undo 表空间管理
- 11. Undo 调优
- 12. 常见坑与排错
- 13. 最佳实践
- 14. 参考资料
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;
关键字段:
| 字段 | 含义 |
|---|---|
XIDUSN | Undo Segment 号 |
XIDSLOT | 事务槽号 |
XIDSQN | 序列号 |
UBAFIL | Undo Block 文件号 |
UBABLK | Undo 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_management | Undo 管理方式(AUTO/MANUAL) | AUTO |
undo_tablespace | Undo 表空间名 | 第一个 Undo 表空间 |
undo_retention | Undo 保留时间(秒) | 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 | 最大并发事务数 |
UNDOTSN | Undo 表空间号 |
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;
解决:
- 联系应用团队确认是否正常
- 必要时 kill session
- 优化应用,避免长事务
坑 4:LOB 列引发 ORA-01555
现象:表有 LOB 列,查询报 ORA-01555。
原因:LOB 列的 Undo 不存在 Undo 表空间,而是存在 LOB 段本身,由 PCTVERSION 或 RETENTION 控制。
解决:
-- 查看 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 TABLE 或 AS 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. 最佳实践
- 必须用 AUM:所有 11g+ 数据库默认[1]
- Undo 表空间 AUTOEXTEND ON:避免 ORA-01555
- 合理设置 undo_retention:参考
v$undostat.maxquerylen[3] - 核心系统启用 Guarantee:强制保留 Undo
- 监控 v$undostat:发现异常及时处理
- 监控 Undo 使用率:阈值 80%
- 应用层避免长事务:分批 commit
- 批量操作分批提交:避免大事务占满 Undo
- LOB 列单独配置 PCTVERSION/RETENTION:避免 ORA-01555
- 闪回场景至少保留 24 小时:
undo_retention = 86400 - OLTP 通常 15-30 分钟:
undo_retention = 900-1800 - ETL 临时调高:批量时段 undo_retention = 3600+
- Undo 表空间与数据文件分开:避免 I/O 争用
- 定期清理旧 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
相关文章