Oracle读写分离本质是应用层路由,需在代码中根据SQL类型手动分发到主库或只读备库,数据库本身不提供原生读写分离功能。
Oracle读写分离本质是应用层路由,不是数据库原生功能
oracle本身不提供像mysql proxy或postgresql pgpool那样的内置读写分离中间件。所谓“.net实现oracle读写分离”,实际是在应用代码或数据访问层中,根据sql类型(select vs insert/update/delete)手动分发到不同连接字符串对应的数据源——一个主库(可读写),多个只读备库(需dba配置为物理/逻辑standby并开启只读模式)。
这意味着:你必须自己控制连接获取逻辑,不能依赖Oracle.ManagedDataAccess自动识别读写意图。
用ConnectionMultiplexer + 自定义路由策略最可控
推荐在仓储层之上加一层轻量路由,避免污染业务代码。以Entity Framework Core为例,不建议直接改DbContext.OnConfiguring硬编码切换,而是通过工厂+策略模式动态注入连接:
- 定义两个
OracleConnectionStringBuilder实例,分别指向主库(Data Source=ORCL_PRIM)和只读库(Data Source=ORCL_STBY) - 创建
IOracleConnectionFactory接口,实现类根据当前上下文决定返回哪个连接:对DbSet<t>.AsNoTracking()</t>或显式标记[ReadOnly]的查询走只读库;所有SaveChanges、ExecuteSqlRaw强制走主库 - 注意事务边界:
TransactionScope内所有操作必须绑定同一连接,此时应自动降级为全走主库,否则抛OracleException提示“distributed transaction not supported”
ADO.NET原生方式要注意连接字符串差异
即使不用ORM,手写OracleCommand时也得小心连接来源。常见坑点:
- 只读库连接字符串必须显式添加
ReadOnly=true(虽然Oracle不校验该参数,但作为语义标记利于排查) - 主库连接建议启用
Validate Connection=true,防止连接池复用失效连接;只读库可关掉,减少心跳开销 - 若使用TNSNAMES.ORA,确保主备库别名不冲突,且
TNS_ADMIN环境变量指向统一目录,否则OracleConnection.Open()可能静默连错 - 不要依赖
CommandType.Text内容做SQL解析判断读写——SELECT COUNT(*) FROM (UPDATE ...)这类嵌套写法会让正则误判
健康检查与故障转移必须自己补全
Oracle没有像SQL Server Always On那样的客户端自动重定向机制。当只读库宕机时,OracleConnection.Open()会抛出Oracle.ManagedDataAccess.Client.OracleException: ORA-12170: TNS:Connect timeout occurred,这时需要:
- 在连接工厂里加入指数退避重试(最多2次),首次失败后快速切回主库执行只读请求(牺牲一致性保可用)
- 用
HealthCheckService定期探测只读库PING(执行SELECT 1 FROM DUAL),结果缓存30秒,避免每次查询都探活 - 日志里必须记录路由决策:比如“[READ] routed to ORCL_STBY (latency=12ms)”、“[FALLBACK] ORCL_STBY unreachable, using ORCL_PRIM”
真正麻烦的从来不是配两个连接字符串,而是让整个调用链路(从HTTP请求进来到DB响应出)对主备差异无感,又能在出问题时快速暴露根因。











