← 返回题目列表

Oracle 的读一致性是怎么实现的?UNDO 有什么作用?

高频 中等 第 5 / 32 题 更新于 2026/07/28
Oracle读一致性UNDOMVCC事务

简化版

Oracle 通过多版本读一致性保证查询看到一致的数据快照。查询开始后,如果某些数据块已经被其他事务修改,Oracle 可以利用 UNDO 中保存的旧版本信息构造出查询时刻的数据。UNDO 既用于一致性读,也用于事务回滚和数据库恢复。

详细版

Oracle 的读一致性核心是:读操作通常不阻塞写操作,写操作通常也不阻塞普通读操作。查询会看到某个时间点的一致视图,而不是边读边看到别人提交的新旧混合结果。

UNDO 的主要作用包括:

  • 保存数据修改前的旧值,支持事务回滚;
  • 为一致性读构造旧版本数据;
  • 崩溃恢复时帮助撤销未提交事务;
  • 支持闪回查询等能力。

例如一个长查询开始后,另一个事务修改并提交了某些行。长查询继续读取时,如果需要看到查询开始时的旧值,Oracle 会根据 UNDO 还原出旧版本。若旧版本所需 UNDO 已被覆盖,就可能出现 ORA-01555: snapshot too old

完整版教学

一、读一致性解决什么问题

没有读一致性时,一个查询可能前半部分读到旧数据,后半部分读到别人刚提交的新数据。对于统计、报表、分页、批量导出,这会造成结果不一致。

Oracle 的一致性读让一条查询看到“同一个时间点”的数据视图。这样即使查询过程中有其他事务提交,当前查询也不会被这些变化打乱。

这就是 Oracle 面试中经常问“读不阻塞写、写不阻塞读”的原因。但这里说的是普通一致性读,不包括显式加锁读。

二、UNDO 保存的是修改前的数据

当事务修改一行时,Oracle 会在 UNDO 中保存修改前的信息。这样如果事务回滚,数据库可以把数据恢复到修改前状态。

例如:

UPDATE account SET balance = balance - 100 WHERE id = 1;

Oracle 会保留这次更新前的余额信息。如果事务执行:

ROLLBACK;

就能利用 UNDO 把余额恢复。

三、UNDO 如何支持一致性读

假设查询 Q 在 10:00:00 开始,事务 T 在 10:00:05 修改并提交了某行。Q 在 10:00:10 扫描到这行时,磁盘块里可能已经是新值。

但 Q 需要看到 10:00:00 的快照,所以 Oracle 会沿着数据块里的事务信息找到相关 UNDO,把这行还原成查询开始时可见的旧版本。

这个过程不是复制整张表,而是按需要构造一致版本。

四、为什么会出现 snapshot too old

如果一个查询运行很久,而系统又有大量更新,旧版本需要的 UNDO 可能已经被复用覆盖。此时 Oracle 无法再构造查询开始时的旧版本,就可能报:

ORA-01555: snapshot too old

常见原因包括:

  • 长查询运行时间太久;
  • UNDO 表空间太小;
  • 更新量太大;
  • 提交过于频繁导致旧版本很快被覆盖;
  • UNDO 保留策略不足以覆盖长查询需求。

解决思路通常是优化长查询、增大 UNDO 表空间、合理设置 UNDO 保留时间、降低不必要的大批量更新压力。

五、UNDO 和 REDO 的区别

很多人会把 UNDO 和 REDO 混在一起。可以这样区分:

类型关注点作用
UNDO修改前的数据回滚、一致性读、撤销未提交事务
REDO修改过程的重做记录崩溃恢复、保证已提交修改可重放

UNDO 让数据库“能退回去”,REDO 让数据库“能重做回来”。Oracle 的事务可靠性和一致性,两者都离不开。

六、常见误区与追问

机制保存/使用什么主要用途
UNDO修改前的数据回滚、一致性读、撤销未提交事务
REDO修改的重做记录崩溃恢复、重放已提交修改
一致性读查询开始时快照普通读不被新提交打乱

记忆钩子:UNDO 让 Oracle “回到旧版本”,REDO 让 Oracle “重做已提交修改”。读一致性靠的是按需用 UNDO 构造旧版本。

用时间线理解 ORA-01555:查询 Q 10:00 开始扫描 1 亿行,事务 T 在 10:05 到 10:20 持续更新同一批表块并提交;Q 到 10:30 才读到这些块,需要 10:00 的旧版本,但对应 UNDO 已被新事务复用覆盖,就可能报 snapshot too old。这个错误不是“查询读错了”,而是旧快照所需版本已经不可构造。

  • 误区:Oracle 普通查询会等写事务释放锁。 普通一致性读通常利用 UNDO 看旧版本,不需要等正在提交后的新值。
  • 误区:UNDO 只用于回滚。 UNDO 还支持一致性读、闪回查询和恢复中撤销未提交事务。
  • 误区:snapshot too old 是表坏了。 它通常表示长查询需要的旧版本 UNDO 已被覆盖,和 UNDO 保留、更新压力、查询耗时有关。
  • 追问:一致性读和当前读有什么区别? 一致性读看快照版本;加锁读或 DML 更关注当前可修改版本和锁状态。
  • 追问:UNDO 表空间太小会怎样? 旧版本更容易被覆盖,长查询和闪回能力受影响,也可能增加 ORA-01555 风险。
  • 追问:UNDO 和 REDO 如何配合恢复? REDO 重放已提交修改,UNDO 撤销未提交修改,二者共同保证崩溃后事务一致性。

七、加强记忆

Oracle 读一致性的主线是:查询要看一个稳定快照,数据被别人改了也没关系,Oracle 可以用 UNDO 构造旧版本。UNDO 记住修改前的数据,服务回滚、一致性读和恢复;长查询需要旧版本但 UNDO 不够时,就可能遇到 snapshot too old。