ADG备库不会自动分担读请求,必须显式路由:需为只读业务单独定义服务名(如rac_ro),在备库用srvctl注册并绑定实例,配置service_names、重启监听,客户端须显式连接该服务名且适配只读语义(如JDBC设oracle.jdbc.readOnly=true)。
ADG备库不会自动分担读请求,必须显式路由
oracle data guard 本身不提供任何负载均衡能力,alter database open read only 只是让备库可查,但客户端连过去仍需明确指定目标。如果你在 tnsnames.ora 里继续用主库的 service_name(比如 orcl),哪怕备库已 open read only,所有连接仍会落到主库——因为监听器和 tns 解析根本不知道“这个请求本该发给备库”。
真正起作用的是应用层或中间件的显式选择:你得为只读业务单独定义一个服务名(如 report 或 rac_ro),并在备库上注册、监听、暴露,然后让应用连接它。
- 备库必须用
srvctl add service -d <db_unique_name> -s report -r <instance_name>注册服务(RAC 环境加-r绑定实例) - 该服务名需加入备库的
service_names参数,并重启监听器(lsnrctl reload或srvctl stop/start listener) -
lsnrctl status输出中必须能看到Service "report"条目,否则客户端无法解析 - Java 应用不能复用主库连接池,JDBC URL 必须指向
report,例如jdbc:oracle:thin:@mydb_report
READ_ONLY_ROUTING_LIST 是个常见误解点
很多人在主库监听器上配了 READ_ONLY_ROUTING_LIST,就以为客户端连主库时,只读请求会自动被甩到备库。这是错的。READ_ONLY_ROUTING_LIST 只对监听器内部生效,且前提是客户端主动声明“我要只读”——而标准 JDBC、SQL*Plus、大多数 ORM 都不会发这种声明。
换句话说,这个参数不是魔法开关,它只影响那些明确支持并启用了只读路由协议的客户端(比如某些版本的 Oracle Client + 特定 tnsnames.ora 配置),生产环境几乎不可靠。
- 不要依赖
READ_ONLY_ROUTING_LIST实现读写分离,它不适用于 Java、Python 或通用 SQL 工具 - 主库监听器配置该参数后,
lsnrctl status也不会显示任何“已启用只读路由”的提示,容易误判生效 - 即使配置正确,若客户端未在连接时设置
read_only=true(JDBC)或未触发只读事务语义,请求仍走主库
客户端必须适配只读语义,否则查询直接失败
备库 open read only 后,SELECT 报 ORA-16000 "database open for read-only access" 不是错误,而是正常状态码。但很多客户端把它当异常抛出,本质是没处理只读库的响应契约。
关键不在数据库端,而在连接建立后的第一句交互:是否允许隐式事务、是否尝试设置事务隔离级别、是否默认开启 autocommit —— 这些行为在只读库上都会触发校验失败。
- JDBC 驱动(尤其 ojdbc8 21.x 之前)需显式设
oracle.jdbc.readOnly=true,否则setReadOnly(true)会反查主库状态并报错 - Python
oracledb必须在connect()后立即执行connection.autocommit = True,否则首次execute()可能隐式 BEGIN TRANSACTION - SQL*Plus 中避免执行
SET TRANSACTION READ ONLY,它强制串行化路径,在 ADG 备库上直接报ORA-01456 - ShardingSphere/MyCat 等中间件默认按主库协议建连,需关闭
auto-commit检测或改用原生 JDBC 连接池
RAC + ADG 组合下服务漂移必须手动保障
用 DGMGRL 执行 SWITCHOVER TO 后,原主库变备库,但 RAC 中你注册的只读服务(如 rac_ro)不会自动停在旧节点、也不会自动起在新备库节点上——除非你当初注册时指定了 -P AUTOMATIC。
手工用 srvctl add service 添加的服务,默认是 Management Policy: MANUAL,角色切换后完全静默,应用连 rac_ro 就会连不上。
- 检查服务策略:
srvctl config service -d <db_name>,确认输出含Management Policy: AUTOMATIC - 若为
MANUAL,需先srvctl remove service,再用-P AUTOMATIC重新添加 - 切换后立刻验证:
srvctl status service -d <db_name>看服务是否运行在当前备库实例上 - 别忘了检查新备库的
service_names和监听器状态,漏一步就会导致服务不可见

















