Oracle 19c ADG 本身不自动实现读写分离,报表系统必须显式连接到备库的只读服务(service_name),且需关闭事务自动开启、禁用隐式 DML、绕过 ORM 的只读校验逻辑——否则所有查询仍会打到主库,甚至报 ORA-16000 被中断。
直接说结论:oracle 19c adg 本身不自动实现读写分离,报表系统必须显式连接到备库的只读服务(service_name),且需关闭事务自动开启、禁用隐式 dml、绕过 orm 的只读校验逻辑——否则所有查询仍会打到主库,甚至报 ora-16000 被中断。
为什么连上备库还是查主库?
ADG 没有流量分发能力,tnsnames.ora 或 JDBC URL 中若仍使用主库的 DB_UNIQUE_NAME 或默认 service(如 orcl),客户端连接时监听器会按 local_listener 和 remote_listener 配置把请求路由回主库实例。RAC + ADG 场景下尤其明显:备库没注册专用 service,lsnrctl status 根本看不到对应条目。
实操建议:
- 在备库用
srvctl add service -d <db_unique_name> -s rac_ro -r <standby_instance> -P BASIC创建只读 service,并加-y AUTOMATIC确保角色切换后自动漂移 - 确认备库
init.ora中设置了service_names=rac_ro,重启监听器使lsnrctl services可见该 service - JDBC 连接串必须显式指定
service_name=rac_ro,不能依赖tnsnames.ora中的别名指向 DB_UNIQUE_NAME - 避免用
jdbc:oracle:thin:@//host:port/SID格式——SID 绑定实例,不支持 service 路由
报表查询报 ORA-16000 怎么办?
ORA-16000 "database open for read-only access" 是正常状态提示,不是错误。但很多客户端把它当异常处理,比如 Spring Boot 默认的 HikariCP 在初始化连接时调用 connection.isReadOnly(),触发底层驱动反查主库状态,导致失败。
实操建议:
- JDBC 驱动必须加参数
oracle.jdbc.readOnly=true(ojdbc8 21.x 后版本才默认兼容 ADG 只读语义) - 连接建立后立即执行
connection.setReadOnly(true),不要依赖连接池自动设置 - Python
oracledb需设autocommit=True,否则首次execute()会隐式开启事务,触发ORA-16000 - 禁用 Hibernate 的
hibernate.connection.isolation自动检测,改用@Transactional(readOnly = true)显式控制
能不能让报表偶尔更新缓存表?
可以,但必须启用 ADG_REDIRECT_DML,且接受它本质是“主库执行 + 备库延迟可见”的行为。这不适用于高频或强一致性要求的写操作。
实操建议:
- 系统级启用:
ALTER SYSTEM SET ADG_REDIRECT_DML=TRUE SCOPE=BOTH SID='*';(主备库都要设) - 会话级更安全:
ALTER SESSION ENABLE ADG_REDIRECT_DML;,仅对当前连接生效 - 注意:PL/SQL 块内 DML 也重定向,但
COMMIT返回前,备库不会应用 redo;其他会话查不到未提交数据 - XA 事务、全局临时表、物化视图日志表上的 DML 不支持重定向,会直接报错
- 监控
v$transaction和v$session_longops,重定向 DML 的等待事件是db file sequential read(走 dblink)而非本地写
最易被忽略的一点:ADG 重定向 DML 依赖内部 DB Link,而这个 link 的目标是主库的 service name。如果主库监听没注册该 service,或网络 ACL 拦截了备库到主库的 1521 端口出向连接,DML 会卡死在 enq: TX - row lock contention,而不是报错——现象是 insert 没响应、commit 不返回,查 v$session_blockers 也看不到阻塞链。


















