oracle data guard本身不提供透明应用切换能力,必须依赖客户端配置(如tnsnames.ora中启用failover=on、service_name匹配动态服务)与数据库端dbms_service+触发器联动实现;jdbc thin driver不支持taf回调,failover=true参数在dg场景下无效。

Oracle Data Guard 本身不提供“透明应用切换”能力,必须靠客户端配置 + 数据库服务层配合实现;JDBC Thin Driver 不支持 TAF 回调,failover=true 参数在 Data Guard 场景下基本无效。
tnsnames.ora 必须启用 FAILOVER + SERVICE_NAME,不能只写 HOST
客户端连接字符串若只指向单个实例(如 (HOST = primary-db)),主库宕机后连接直接失败,没有任何重试逻辑。真正起作用的是带故障转移语义的完整描述:
-
FAILOVER = ON是开关,不加它整个 failover_mode 块被忽略 -
SERVICE_NAME必须是数据库里真实创建的服务名(如PROD_TAF),不是DB_UNIQUE_NAME或SID - 两个
ADDRESS块需分别指向主、备库监听地址,且端口一致;LOAD_BALANCE = off避免连接随机打到未激活角色的实例上
示例片段(注意 service_name 和 address 的对应):
PROD_TAF = (DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = primary-host)(PORT = 1521))
(ADDRESS = (PROTOCOL = TCP)(HOST = standby-host)(PORT = 1521))
(LOAD_BALANCE = off)
(FAILOVER = ON)
(CONNECT_DATA =
(SERVICE_NAME = PROD_TAF)
(FAILOVER_MODE =
(TYPE = SELECT)
(METHOD = BASIC)
(RETRIES = 5)
(DELAY = 3)
)
)
)
数据库端必须用 DBMS_SERVICE + 触发器动态启停服务
单纯在 tnsnames 里配双地址没用——如果备库没启动 PROD_TAF 服务,客户端连过去会报 ORA-12514: TNS:listener does not currently know of service requested。服务必须随角色自动启停:
- 主库和备库都执行
EXEC DBMS_SERVICE.CREATE_SERVICE('PROD_TAF', 'PROD_TAF') - 创建触发器,在数据库启动后检查
V$DATABASE.DATABASE_ROLE,仅当为PRIMARY时调用DBMS_SERVICE.START_SERVICE - 切完 Switchover 后,原主库变 standby,触发器不会启动服务;新主库启动时自动拉起服务,客户端下次连接即可命中
关键点:触发器必须是 AFTER STARTUP ON DATABASE,不能用 BEFORE ——否则 V$DATABASE 还不可查。
JDBC Thin Driver 的 failover 参数在 DG 场景下形同虚设
很多文档说加 failover=true&loadBalance=true 就能自动切,这是对 RAC 的误解。Data Guard 主备切换后,IP/端口没变但角色已翻转,Thin Driver 不感知数据库内部状态,也不会主动重连新主库。它只在首次建连失败时尝试下一个 ADDRESS,之后所有连接复用已有连接池,而连接池里的连接仍连着旧主库(此时已只读或关闭),导致后续 SQL 报 ORA-01033: ORACLE initialization or shutdown in progress 或 ORA-03113: end-of-file on communication channel。
- Spring Boot 应用必须用
AbstractRoutingDataSource自定义路由,根据健康检查结果(如查询SELECT DATABASE_ROLE FROM V$DATABASE)决定走哪个数据源 - 连接池(如 HikariCP)要配置
connection-test-query = SELECT 1 FROM DUAL和合理validation-timeout,确保坏连接被及时剔除 - 不能依赖驱动自带的
failover,那是给 OCI 用的,Thin Driver 只做最基础的地址轮询
Switchover 完成后,应用端 DNS 或连接池不会自动刷新
即使 tnsnames 配置正确,应用启动后已加载该文件并缓存解析结果。如果用的是 Oracle Wallet 或 JDBC URL 直接写死地址,切换后必须重启应用或手动触发连接池刷新。更稳妥的做法是:
- 把
tnsnames.ora放在共享路径,应用通过TNS_ADMIN环境变量指向它,并启用sqlnet.ora中的NAME.DEFAULT_ZONE = world和SQLNET.EXPIRE_TIME = 10加速连接失效探测 - 避免用
jdbc:oracle:thin:@//host:port/service这种直连格式,强制走tnsnames.ora查找 - 切完立刻检查监听器状态:
lsnrctl service | grep PROD_TAF,确认服务只在当前主库上注册成功
最容易被忽略的是:Switchover 后原主库虽然变成 standby,但它默认仍注册了同名服务(除非触发器明确 stop),导致客户端可能连上只读实例却误以为是可写主库——这个坑比连不上还危险。











