🌺The Begin🌺点点关注,收藏不迷路🌺


一、开篇奇迹:Oracle如何让你看到“过去”的数据?

假设现在是上午10:00,你执行了一条查询SELECT * FROM orders WHERE order_id = 100。与此同时,另一个会话在10:01执行了UPDATE orders SET status = 'SHIPPED' WHERE order_id = 100并提交。

神奇的事情发生了:你的查询仍然返回修改前的旧值!Oracle仿佛拥有“时间旅行”的能力,让你看到了10:00时刻的数据快照。

这就是Oracle的读一致性(Read Consistency) 机制。而支撑这一机制的幕后英雄,就是UNDO表空间——它存储着数据修改前的“旧版本”,让每个查询都能看到一致的数据快照。

今天这篇文章,带你深入UNDO表空间的内部,彻底搞懂回滚段、读一致性和闪回查询的实现原理。


二、全景架构图:UNDO在Oracle体系中的位置

先通过一张架构图,看清UNDO表空间在整个Oracle架构中的角色:

应用场景

REDO机制

UNDO机制

SGA内存区

Buffer Cache

数据块

修改数据块

脏块-Dirty Buffer

生成UNDO记录

UNDO表空间

回滚段 Undo Segment

UNDO块 Undo Block

旧值: status='PENDING'

生成REDO记录

Redo Log Buffer

联机重做日志

事务回滚 ROLLBACK

读一致性 Read Consistency

闪回查询 Flashback Query

实例恢复 Instance Recovery

关键解读:

  • 蓝色(Buffer Cache): 数据修改的第一现场。
  • 橘色(UNDO表空间): 存储数据修改前的旧值。
  • 红色(回滚段): UNDO表空间中的管理单元。
  • 绿色(回滚): 事务回滚需要UNDO。
  • 紫色(读一致性): 多版本并发控制的核心。

三、UNDO表空间的本质定义

1. UNDO是什么?

精确描述:
UNDO表空间是Oracle数据库中用于存储数据修改前镜像(Before Image) 的专用表空间。每次UPDATE、DELETE或INSERT操作,Oracle都会在修改数据块之前,先将原始值记录到UNDO表空间中,然后才修改数据块。

UNDO记录的三大核心内容:

内容 说明 示例
UNDO记录头 事务ID、SCN、操作类型 XID: 8.15.12345
行标识 被修改行的ROWID ROWID: AAADd+
旧值 修改前的列值 salary: 1000 → 旧值=1000

2. UNDO的四大核心用途

用途一:事务回滚(Rollback)

-- 如果事务失败或用户主动回滚
ROLLBACK;
-- Oracle从UNDO表空间中读取旧值,将数据恢复到修改前的状态

用途二:读一致性(Read Consistency)

-- 查询开始时,Oracle记录当前SCN
-- 如果读取的数据块SCN大于查询的SCN
-- Oracle从UNDO中找到该行的旧版本(SCN小于查询SCN的版本)

用途三:闪回查询(Flashback Query)

-- 查询10分钟前表的数据
SELECT * FROM orders AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '10' MINUTE);
-- Oracle从UNDO中找到10分钟前的数据版本

用途四:实例恢复(Instance Recovery)

-- 数据库崩溃后重启
-- Oracle需要回滚崩溃时未提交的事务
-- 这些事务的UNDO记录仍然在UNDO表空间中

四、回滚段(Undo Segment)深度解析

1. 回滚段的本质定义

精确描述:
回滚段是UNDO表空间中用于存储UNDO记录的逻辑结构每个活跃的事务会分配到一个回滚段,该事务的所有UNDO记录都写入这个回滚段中。

回滚段的结构:

回滚段(Undo Segment)
├── 段头块(Segment Header)
│   ├── 事务表(Transaction Table)
│   │   ├── 事务槽1:XID + 状态 + UBA
│   │   ├── 事务槽2:XID + 状态 + UBA
│   │   └── 事务槽N...
│   └── 空闲空间列表
├── UNDO块1
├── UNDO块2
└── UNDO块N(循环使用)

2. 回滚段中的事务表(Transaction Table)

事务表是回滚段段头块中的核心结构,记录了正在使用该回滚段的所有事务信息。

事务表的关键字段:

字段 含义 示例
XID 事务ID 8.15.12345
XIDUSN 回滚段编号 8
XIDSLOT 事务槽编号 15
XIDSQN 序列号 12345
UBA UNDO块地址(指向最新UNDO记录) 0x004001a2.0012.00045
STATUS 事务状态 ACTIVE/COMMITTED/DEAD

查询当前活跃事务的事务表:

-- 查看活跃事务的UNDO使用情况
SELECT 
    s.sid,
    s.username,
    t.xidusn,
    t.xidslot,
    t.xidsqn,
    t.used_ublk AS undo_blocks,
    t.used_urec AS undo_records,
    t.status
FROM v$transaction t
JOIN v$session s ON t.ses_addr = s.saddr
ORDER BY t.used_ublk DESC;

3. 回滚段的种类

系统回滚段(SYSTEM Rollback Segment):

  • 存储在SYSTEM表空间中。
  • 用于系统事务(如数据字典的修改)。
  • 由Oracle自动管理。

非系统回滚段(Non-SYSTEM Rollback Segment):

  • 存储在UNDO表空间中。
  • 用于用户事务。
  • 可自动或手动管理。

AUM(自动UNDO管理)模式:

-- 查看当前UNDO管理模式
SHOW PARAMETER undo_management;

-- AUTO表示自动管理
-- MANUAL表示手动管理(已过时)

自动UNDO管理的工作原理:

-- Oracle自动创建和管理回滚段
-- 根据事务负载自动调整回滚段数量
-- UNDO_RETENTION参数控制UNDO保留时间

ALTER SYSTEM SET UNDO_RETENTION = 900;  -- 保留15分钟(秒为单位)
ALTER TABLESPACE undotbs1 RETENTION GUARANTEE;  -- 强制保留

五、读一致性(Read Consistency)深度解析

1. 读一致性的本质定义

精确描述:
读一致性是Oracle多版本并发控制(MVCC)的核心机制,确保每个查询看到的数据是查询开始时刻的一致性快照,不受其他并发事务的影响。

读一致性的三大保证:

  1. 语句级读一致性: 一条SQL语句执行期间看到的数据是一致的。
  2. 事务级读一致性: 一个事务中的所有SQL看到的数据是一致的(通过SET TRANSACTION READ ONLY)。
  3. 多版本并发控制: 写操作不阻塞读操作,读操作不阻塞写操作。

2. 读一致性的完整实现流程

UNDO表空间 ITL事务槽 数据块 查询-事务A UNDO表空间 ITL事务槽 数据块 查询-事务A alt [事务SCN <= 1000-事务在查询前已提交] [事务SCN > 1000-事务在查询后修改] alt [块SCN <= 1000] [块SCN > 1000-有并发修改] 1.查询开始,记录SCN=1000 2.读取数据块 3.检查块的SCN 4a.直接返回数据(无并发修改) 4b.查找修改该行的事务槽 5.检查事务的SCN 6a.返回当前数据(已提交的修改可见) 6b.通过ITL的UBA找到UNDO记录 7.沿着UNDO链向前查找 8.找到SCN<=1000的旧版本 9.返回旧版本数据(CR Consistent Read)

关键步骤详解:

步骤3-检查块SCN:

  • 每个数据块头部都有该块最后一次修改的SCN。
  • 如果块SCN <= 查询SCN,说明块在查询开始前已是最新,直接返回。

步骤5-检查事务SCN:

  • 如果块SCN > 查询SCN,说明查询开始后有人修改了该块。
  • 通过ITL事务槽找到修改该行的事务信息。

步骤7-沿着UNDO链查找:

  • Oracle从最新的UNDO记录开始,沿着UNDO链向前查找。
  • 直到找到第一个SCN <= 查询SCN的版本。

步骤9-返回CR副本:

  • 在Buffer Cache中构造一致性读副本(CR Copy)。
  • 将旧版本数据返回给查询。

3. 读一致性的实际操作演示

场景设置:

-- 准备数据
CREATE TABLE accounts (id NUMBER PRIMARY KEY, balance NUMBER);
INSERT INTO accounts VALUES (1, 1000);
INSERT INTO accounts VALUES (2, 2000);
COMMIT;

时间线演示:

-- 时间 T1(10:00:00):会话A开始查询
-- 会话A:
SELECT balance FROM accounts WHERE id = 1;
-- 此时balance = 1000,查询SCN = 12345

-- 时间 T2(10:00:05):会话B修改并提交
-- 会话B:
UPDATE accounts SET balance = 1500 WHERE id = 1;
COMMIT;
-- 修改SCN = 12350

-- 时间 T3(10:00:10):会话A的查询仍在进行中
-- 会话A的查询读到id=1的数据块时:
-- 块SCN = 12350 > 查询SCN 12345
-- Oracle通过UNDO找到SCN=12345时的旧值:balance=1000
-- 返回1000(而不是1500)

这就是Oracle读一致性的核心价值:查询不受并发修改的影响,总是看到查询开始时刻的一致性快照。


六、闪回查询(Flashback Query)深度解析

1. 闪回查询的本质定义

精确描述:
闪回查询是利用UNDO表空间中的历史数据,查询表在过去的某个时间点或SCN时的数据状态。它不是真的“时间旅行”,而是利用UNDO记录重建过去的数据版本。

闪回查询的语法:

-- 按时间戳闪回
SELECT * FROM orders 
AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '10' MINUTE);

-- 按SCN闪回
SELECT * FROM orders 
AS OF SCN 1234567;

2. 闪回查询的实现原理

否-UNDO已被覆盖

闪回查询: 查询10分钟前的数据

确定10分钟前的SCN

扫描表的数据块

块SCN <= 目标SCN?

直接读取数据块中的当前行

通过ITL找到UNDO记录

沿着UNDO链向前查找

找到SCN<=目标SCN的版本?

返回旧版本数据

报错: ORA-01555 快照过旧

闪回查询的SCN确定:

-- 将时间戳转换为SCN
SELECT TIMESTAMP_TO_SCN(SYSTIMESTAMP - INTERVAL '10' MINUTE) AS scn
FROM dual;

-- 或者直接使用时间表达式
SELECT * FROM orders 
AS OF TIMESTAMP SYSTIMESTAMP - INTERVAL '10' MINUTE;

3. ORA-01555快照过旧的成因与解决

ORA-01555的产生原因:

1. 查询开始于T1时刻
2. 查询需要读取某个数据块的旧版本(T1之前的状态)
3. 该数据块被频繁修改,产生了大量UNDO记录
4. UNDO表空间空间不足,旧的UNDO记录被覆盖
5. 查询无法找到T1时刻的旧版本 → ORA-01555

解决方案:

方案一:增大UNDO表空间

-- 增大UNDO表空间
ALTER DATABASE DATAFILE '/u01/oradata/ORCL/undotbs01.dbf' RESIZE 10G;
-- 或添加新数据文件
ALTER TABLESPACE undotbs1 ADD DATAFILE '/u02/oradata/ORCL/undotbs02.dbf' SIZE 5G;

方案二:延长UNDO_RETENTION

-- 设置UNDO保留时间为30分钟
ALTER SYSTEM SET UNDO_RETENTION = 1800;

-- 强制保证保留时间(UNDO表空间足够大时)
ALTER TABLESPACE undotbs1 RETENTION GUARANTEE;

方案三:优化查询

-- 缩短查询执行时间
-- 1. 添加索引
-- 2. 优化SQL
-- 3. 分批处理大查询

七、UNDO表空间的监控与调优

1. 查看UNDO表空间使用情况

-- 查看UNDO表空间大小和使用率
SELECT 
    tablespace_name,
    ROUND(SUM(bytes)/1024/1024/1024, 2) AS total_gb,
    ROUND(SUM(bytes)/1024/1024/1024 - 
          (SELECT SUM(bytes)/1024/1024/1024 
           FROM dba_free_space 
           WHERE tablespace_name = 'UNDOTBS1'), 2) AS used_gb
FROM dba_data_files
WHERE tablespace_name LIKE '%UNDO%'
GROUP BY tablespace_name;

-- 查看UNDO表空间的空闲空间
SELECT tablespace_name, 
       ROUND(SUM(bytes)/1024/1024, 2) AS free_mb
FROM dba_free_space
WHERE tablespace_name LIKE '%UNDO%'
GROUP BY tablespace_name;

2. 查看UNDO的保留情况

-- 查看UNDO_RETENTION设置
SHOW PARAMETER undo_retention;

-- 查看实际UNDO保留时间(优化建议值)
SELECT 
    ROUND(MAX(maxquerylen), 2) AS max_query_sec,
    ROUND(MAX(undoblks) * 8 / 1024, 2) AS max_undo_mb
FROM v$undostat;

-- 查看TUNED_UNDORETENTION(Oracle自动调整的保留时间)
SELECT 
    TO_CHAR(begin_time, 'HH24:MI:SS') AS begin_time,
    undoblks,
    tuned_undoretention,
    maxquerylen
FROM v$undostat
ORDER BY begin_time DESC
FETCH FIRST 20 ROWS ONLY;

关键指标:

  • tuned_undoretention:Oracle建议的UNDO保留时间(秒)。
  • maxquerylen:最长的查询持续时间(秒)。
  • 如果tuned_undoretention > undo_retention,说明需要增大UNDO_RETENTION。

3. 查看活跃事务的UNDO使用

-- 查看当前活跃事务的UNDO使用量
SELECT 
    s.sid,
    s.username,
    s.program,
    t.used_ublk AS undo_blocks,
    t.used_urec AS undo_records,
    ROUND(t.used_ublk * 8 / 1024, 2) AS undo_mb,
    t.start_date,
    ROUND((SYSDATE - t.start_date) * 24 * 60, 2) AS elapsed_minutes
FROM v$transaction t
JOIN v$session s ON t.ses_addr = s.saddr
ORDER BY t.used_ublk DESC;

诊断标准:

  • 单个事务UNDO使用 > 1GB:需要关注。
  • 事务运行时间 > 30分钟:需要检查是否是长事务。

4. UNDO表空间大小建议

计算公式:

UNDO表空间大小 = UR × UPS × DB_BLOCK_SIZE

UR = UNDO_RETENTION(秒)
UPS = 每秒产生的UNDO块数
DB_BLOCK_SIZE = 块大小(字节)

实际查询:

-- 计算UNDO表空间的建议大小
SELECT 
    ROUND((MAX(undoblks) * 8 * 1024 * 1024) / 1024 / 1024 / 1024, 2) AS suggested_undo_gb
FROM v$undostat;

八、总结:记住这个“修改前的照片”类比就够了

【UNDO是数据修改前的照片,REDO是数据修改后的照片】

  • UNDO(修改前的照片): 每次修改数据前,Oracle先拍一张“修改前照片”存到UNDO表空间。 如果后悔了(ROLLBACK),用这张照片恢复原样。如果查询开始得早,需要看老照片,UNDO提供历史版本。
  • REDO(修改后的照片): 每次修改数据后,Oracle记录“修改后照片”到REDO日志。 如果数据库崩溃,用REDO重新修改一遍(前滚)。
  • 读一致性: 查询开始时刻,UNDO是历史照片的相册。查询只需要看“相册”中与自己同时刻的照片。
  • 闪回查询: 想看10分钟前的照片?从UNDO相册中翻出来。
  • ORA-01555: 相册空间满了,老照片被撕掉了,找不到10分钟前的照片了。

理解了UNDO的四大用途(回滚、读一致性、闪回查询、实例恢复),你就掌握了Oracle多版本并发控制的精髓。合理配置UNDO表空间大小和UNDO_RETENTION,是DBA日常运维的基本功。

你在日常运维中是否遇到过ORA-01555错误?是如何解决的?UNDO_RETENTION设置为多少?欢迎在评论区分享你的经验。


在这里插入图片描述


🌺The End🌺点点关注,收藏不迷路🌺

Logo

葡萄城是专业的软件开发技术和低代码平台提供商,聚焦软件开发技术,以“赋能开发者”为使命,致力于通过表格控件、低代码和BI等各类软件开发工具和服务

更多推荐