只读事务(READ ONLY)是Oracle中通过SET TRANSACTION READ ONLY声明的会话级事务,禁止DML操作并基于启动时SCN提供一致性快照,适用于报表查询、ETL核对、跨表校验等需时间点数据的场景。

什么是只读事务(READ ONLY)及其适用场景
Oracle 中的 SET TRANSACTION READ ONLY 不是“角色”,而是一个会话级事务控制语句,用于将当前事务显式声明为只读。它不依赖用户权限或角色配置,而是运行时行为约束——一旦启用,该事务内所有 DML(INSERT/UPDATE/DELETE)都会被拒绝,从根本上防止意外提交脏数据或破坏一致性。
典型适用场景包括:报表查询、ETL抽取阶段的数据核对、跨多个表的联合校验逻辑、审计类脚本执行前的快照隔离。注意:它不能替代应用层的权限管控,也不能阻止 DDL 或 SELECT FOR UPDATE(后者会报错)。
如何正确启用只读事务并避免常见错误
必须在事务开始后、任何 DML 之前执行,且不能在已执行 DML 的会话中追加设置。否则 Oracle 报错:ORA-01453: SET TRANSACTION must be first statement of transaction。
-
SET TRANSACTION READ ONLY必须是当前事务中的第一条语句;如果之前执行过SELECT(非查询语句如DESC或SHOW不算),仍可设置 - 若已执行过
INSERT等 DML,需先COMMIT或ROLLBACK,再重新开启事务并第一时间设置 - 只读事务下执行
UPDATE emp SET sal = sal * 1.1会立即报错:ORA-01456: may not perform insert/delete/update operation inside a read-only transaction - 该设置仅对当前事务有效,
COMMIT后下个事务需重新设置
与 READ COMMITTED 隔离级别的关键区别
很多人误以为把事务设为 READ COMMITTED 就能防写,其实完全不是一回事。READ COMMITTED 是默认隔离级别,允许读已提交数据,也完全允许 DML 和 COMMIT;而 READ ONLY 是强制性写保护机制,连 DML 解析都失败。
对比示例:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 允许后续 UPDATE/COMMIT SET TRANSACTION READ ONLY; -- 后续任何 UPDATE 都直接报错
另外,READ ONLY 事务启动时会获取一个 SCN 快照,整个事务期间所有 SELECT 都基于该 SCN —— 这意味着它天然具备“查询一致性”,无需额外加 AS OF SCN。
生产环境使用时容易忽略的细节
最常被跳过的一步是:未确认当前是否已在事务中。Oracle 的事务起点是第一条 DML,但很多 DBA 习惯用 SELECT 开头探路,误以为“还没进事务”,结果一执行 SET TRANSACTION READ ONLY 就报错。
安全做法是:在执行任何 SQL 前,先运行 SELECT SYS_CONTEXT('USERENV', 'TXN_ID') FROM DUAL;。若返回空值,说明尚未开启事务,可安全设置;若返回类似 0x000A.0000000F.00000000 的值,则必须先 ROLLBACK。
还有两点硬限制:不能在 PL/SQL 匿名块中直接写 SET TRANSACTION(需用 EXECUTE IMMEDIATE);也不能在开启了 AUTOCOMMIT 的工具(如某些 JDBC 驱动配置)中生效——因为每次语句执行完自动提交,事务生命周期太短。


















