Oracle 读一致性与 CR 块详解

Oracle 读一致性与 CR 块详解

适用版本:Oracle Database 11g / 12c / 19c / 23ai 文档版本:v1.0 / 2026-07


1. 概述

读一致性(Read Consistency) 是 Oracle 保证查询结果在某个时间点一致性的机制[1]。

核心机制:通过 CR 块(Consistent Read Block) 实现,CR 块是从 Undo 数据中构造的旧版本数据块。

两种读一致性

类型范围默认
语句级单条 SQL 执行期间
事务级整个事务SERIALIZABLE/READ ONLY 模式

2. CR 块构造流程

2.1 触发条件

查询访问数据块时,比较块的 SCN 与查询 SCN:

If 块 SCN <= 查询 SCN:
    直接读当前块(无需构造 CR 块)
Else:
    需要构造 CR 块(应用 Undo 数据回滚)

2.2 构造步骤

1. 从 Buffer Cache 中找到当前数据块
2. 检查块头 ITL,找到符合 SCN 要求的事务槽
3. 从该 ITL 槽指向的 Undo 段读取 Undo 记录
4. 在 Buffer Cache 中克隆一个 CR 块(不覆盖原块)
5. 对 CR 块应用 Undo 记录,回滚到目标 SCN
6. 若仍不够旧,沿 Undo 链继续回滚
7. 返回 CR 块给查询

2.3 Undo 链遍历

当前块(SCN=1010)

   ▼ 应用 Undo 记录 1
CR 块(SCN=1008)
   │ 仍 > 查询 SCN=1000
   ▼ 应用 Undo 记录 2
CR 块(SCN=1005)
   │ 仍 > 查询 SCN=1000
   ▼ 应用 Undo 记录 3
CR 块(SCN=0998)  ← 满足要求,返回此块

3. CR 块与 Buffer Cache

CR 块存储在 Buffer Cache 中,但有特殊标记:

-- 查看 Buffer Cache 中的块状态
SELECT 
  status,      -- FREE/XCUR/CR/READ
  COUNT(*) AS blocks
FROM v$bh
GROUP BY status;

-- status='CR' 的就是 CR 块

CR 块特点

  • 只读,不会被写盘
  • 命中率高时减少 Undo 应用开销
  • 空间不足时优先被淘汰

4. 语句级 vs 事务级读一致性

4.1 语句级(默认)

-- 会话 1
SELECT salary FROM emp WHERE id=1;  -- 假设返回 5000

-- 会话 2
UPDATE emp SET salary=6000 WHERE id=1;
COMMIT;

-- 会话 1 再次查询
SELECT salary FROM emp WHERE id=1;  -- 返回 6000(新语句看到新提交)

4.2 事务级(SERIALIZABLE)

-- 会话 1
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT salary FROM emp WHERE id=1;  -- 返回 5000

-- 会话 2
UPDATE emp SET salary=6000 WHERE id=1;
COMMIT;

-- 会话 1 再次查询
SELECT salary FROM emp WHERE id=1;  -- 仍返回 5000(事务级一致)

-- 会话 1 尝试更新
UPDATE emp SET salary=7000 WHERE id=1;
-- ORA-08177: can't serialize access for this transaction

4.3 只读事务

SET TRANSACTION READ ONLY;
-- 整个事务期间看到的数据都是事务开始时的快照
-- 不允许 DML

5. CR 块相关统计

-- 关键统计
SELECT name, value 
FROM v$sysstat 
WHERE name IN (
  'consistent gets',                              -- CR 读总次数
  'consistent gets - examination',                -- 简单 CR 读
  'no work - consistent read gets',               -- 无需 Undo 的 CR 读
  'cleanouts only - consistent read gets',        -- 仅清理的 CR 读
  'data blocks consistent reads - undo records applied',  -- 应用 Undo 次数
  'rollbacks only - consistent read gets'         -- 完全回滚的 CR 读
);

-- 健康指标:
-- consistent gets 中,no work 占比应 > 70%
-- undo records applied 占比过高 = Undo 应用过多 = 可能出现 ORA-01555

6. 常见坑与排错

6.1 ORA-01555: snapshot too old

现象:查询报 ORA-01555: snapshot too old: rollback segment number X with name "..." too small

原因:构造 CR 块需要的 Undo 已被覆盖。

修复

-- 1. 增大 undo_retention
ALTER SYSTEM SET undo_retention=7200 SCOPE=BOTH;

-- 2. 增加 Undo 表空间
ALTER TABLESPACE undotbs1 ADD DATAFILE '/u02/undo02.dbf' SIZE 2G;

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

-- 4. 监控 Undo 使用率
SELECT 
  tablespace_name,
  SUM(bytes)/1024/1024 AS active_mb
FROM dba_undo_extents
WHERE status='ACTIVE'
GROUP BY tablespace_name;

6.2 CR 块构造开销过大

现象:查询慢,CR 构造消耗大量 CPU。

排查

SELECT name, value 
FROM v$sysstat 
WHERE name LIKE '%consistent%';
-- 若 undo records applied 数值高,说明 Undo 应用过多

修复

  • 避免长事务与长查询并行
  • 缩短事务持续时间
  • 调整业务流程,错峰执行

6.3 序列号重复(SERIALIZABLE 模式)

现象:SERIALIZABLE 模式下使用 SEQUENCE 出现重复。

原因:SERIALIZABLE 事务看到的是事务开始时的快照,但 SEQUENCE 不受事务隔离影响。

修复:使用 CACHE 选项预分配序列号。


7. 最佳实践

  1. 设置合理 undo_retention:≥ 业务最长查询时间
  2. 关键业务启用 Guarantee:保证 Undo 不被覆盖
  3. 避免长事务:定期 COMMIT
  4. 批量操作错峰:避免大事务与查询并行
  5. 监控 consistent gets 统计:CR 读中 Undo 应用比例
  6. 使用 AS OF 进行闪回查询:明确指定时间点
  7. 大表查询考虑使用 READ ONLY:明确事务级一致性

8. 参考资料

[1] Oracle Database Concepts 19c, “Read Consistency” https://docs.oracle.com/en/database/oracle/oracle-database/19/cncpt/data-concurrency-and-consistency.html

[2] AskTOM, “CR Block Construction” https://asktom.oracle.com/pls/apex/f?p=100:1:0

[3] Oracle Support Note 40689.1, “ORA-01555 Explanation” https://support.oracle.com/epmos/faces/DocumentDisplay?id=40689.1