Oracle RAC本身不支持自动读写分离,Service无法识别SQL类型路由,role和instance_mode在纯RAC中无效;可行方案是应用层显式区分读写Service,或采用ADG实现真正读写分离。
oracle rac 本身不支持自动读写分离——service 无法识别 sql 类型(比如 select 还是 insert),也不会根据语句类型把请求路由到不同节点。 所有“靠 service 的 role 或 instance_mode 实现读写分离”的尝试,在纯 rac 环境中都会失效,且可能引发实例启动失败或 crs 反复重启。
srvctl add service -r 指定实例 ≠ 读写分离
很多人执行类似这样的命令:
srvctl add service -d orcl -s rw_svc -r "orcl1" -l PRIMARY
以为设置了 -r "orcl1" 和 -l PRIMARY 就能强制写请求只走 orcl1、读请求走 orcl2。但事实是:
-
role=PRIMARY在纯 RAC 中被完全忽略,gv$active_services.role查出来是NULL -
-r参数只表示“首选实例(Preferred Instance)”,不是“独占实例”;负载均衡(尤其是CLB_GOAL=LONG)仍可能把新连接分发到其他可用实例 - 客户端连上
rw_svc后执行任意 SQL,包括SELECT,都只在当前连接所在的实例上执行,不会跨节点重定向 - 哪怕你给两个 Service 分别绑定不同实例,只要应用没做区分,所有连接仍可能落到同一个实例上
真正可行的业务级分流:靠应用显式选择 Service
如果你坚持只用一套 RAC(不引入 ADG/Logical Standby),唯一稳定的方式是让应用层自己决定连哪个 Service,并严格约束 SQL 类型与 Service 的对应关系:
- 为写操作创建专用 Service:
srvctl add service -d orcl -s write-svc -r "orcl1,orcl2" -P BASIC -e SELECT -m BASIC -z 180 -w 5 - 为读操作创建另一个 Service:
srvctl add service -d orcl -s read-svc -r "orcl3,orcl4" -P BASIC -e SELECT -m SHORT -z 180 -w 5 - 应用代码中必须显式使用不同连接串:
write-svc仅用于INSERT/UPDATE/DELETE/DDL;read-svc仅用于SELECT - 禁止在
read-svc连接上执行写语句,否则会报错(如 ORA-16000:database open for read-only access)——前提是已在实例级禁用写(见下一条)
注意:instance_mode=read-only 在 RAC 中不可用;但你可以通过 ALTER DATABASE OPEN READ ONLY 把某个实例强制设为只读(需先停掉该实例、修改参数、再启动),不过这会导致 CRS 认为实例异常并反复拉起——所以更安全的做法是在应用层拦截 + 数据库触发器校验。
为什么不能用 instance_mode=read-only
从 Oracle 12.2 开始,instance_mode 参数理论上支持 read-only,但它:
- 仅在 Data Guard Logical Standby 场景下受支持;RAC 中设置会直接导致实例无法启动,或启动后报
ORA-01102/ORA-600 [krsv_pmon_detach_2] - RAC 要求至少一个实例处于
READ WRITE模式才能维持数据库打开状态,违反此约束时 CRS 会持续尝试恢复,形成“启动→失败→重启”循环 - 即使绕过启动检查,
instance_mode控制的是实例级只读状态,不参与 SQL 路由,也不影响客户端连接行为 - 若已误设,需用
sqlplus / as sysdba连接 idle 实例,执行create pfile from spfile,手动编辑 pfile 删除instance_mode行,再重建 spfile
比“读写分离”更实际的目标:业务分割(Workload Partitioning)
与其强行在 RAC 内部模拟主从读写分离,不如回归 RAC 的设计本意:按业务类型做实例级分流。例如:
- OLTP 事务类请求(短连接、高并发 DML)固定连
svc-oltp,绑定到inst1,inst2 - 报表/ETL 类请求(长连接、大结果集
SELECT)固定连svc-report,绑定到inst3,inst4 - 这样既能减少 Cache Fusion 带来的全局缓存争用(
gc buffer busy acquire),又避免了同步延迟和一致性风险 - 实现方式就是上面提到的
srvctl add service -r+ 应用连接串硬编码,无需改 SQL,也无需依赖任何数据库内部路由机制
关键点在于:这不是“读写分离”,而是“业务分离”——它不承诺数据一致性隔离,但能真实提升 RAC 内部资源利用效率。而真正的读写分离,必须走出 RAC,走向 ADG 或逻辑复制架构。


















