oracle data guard切换后客户端连接中断是常态,需通过address_list配置failover实现透明访问,而非仅修改tns别名;必须同步更新service_name注册、host解析及中间件独立tnsnames.ora,并管控连接池僵尸连接。
oracle data guard 切换后,客户端连接中断是常态,不是配置错误——tnsnames.ora 里写的还是旧主库地址,备库升主后 ip、服务名甚至监听端口都可能不同。要实现“透明访问”,核心不是让客户端自动感知切换,而是提前设计好可复用、可切换的连接结构,并在切换后**最小化修改**客户端配置。
为什么不能只改一个TNS别名就完事?
单纯把 tnsnames.ora 中原来指向主库的条目改成新主库地址,看似简单,但会带来三个现实问题:
- 所有客户端(开发机、应用服务器、中间件)都要同步更新,漏一台就断连
- 切换窗口期存在新旧连接混用,事务可能跨库提交失败
- 下次再切回原主库(Switchover),又得改回来,运维不可持续
真正可行的做法是:用连接描述符的冗余能力,让单个 TNS 别名能同时描述主备两套地址,由客户端驱动自动选路。
用 ADDRESS_LIST 实现故障转移(Failover)
Oracle 原生支持通过 ADDRESS_LIST 定义多个地址,并配合 FAILOVER=on 和 LOAD_BALANCE=off 控制行为。这不是“负载均衡”,而是“主备自动跳转”。
示例(适用于 DG 切换后无需改客户端):
MYDB =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = primary-host)(PORT = 1521))
(ADDRESS = (PROTOCOL = TCP)(HOST = standby-host)(PORT = 1521))
)
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = mydb_svc)
(FAILOVER_MODE =
(TYPE = SESSION)
(METHOD = BASIC)
(RETRIES = 3)
(DELAY = 5)
)
)
)
-
FAILOVER=on(默认)必须显式开启,否则只连第一个地址 -
TYPE = SESSION表示连接断开后重连会尝试下一个地址;SELECT过程中不会自动重试(不支持 SQL 级重试) - 客户端首次连接时,仍优先连
primary-host;只有它不可达时,才 fallback 到standby-host - 注意:该机制依赖客户端驱动(如 Oracle JDBC 12.2+、python-oracledb Thick 模式),Thin 模式不支持完整 FAILOVER_MODE
切换后必须同步更新的三项关键配置
即便用了 ADDRESS_LIST,DG 切换后仍有三处必须人工确认或更新,否则连接会失败:
-
SERVICE_NAME:新主库上是否已注册该服务?执行lsnrctl services查看输出中是否有mydb_svc;若无,需在新主库运行ALTER SYSTEM REGISTER;或检查service_names参数 -
HOST名称解析:确保客户端能解析primary-host和standby-host——不要用 SCAN 名或 VIP,DG 场景下它们不跨角色生效;推荐用静态 IP 或明确指向单实例的主机名(如rac01、dg-standby01) -
tnsnames.ora文件位置:WebLogic、Tomcat 等中间件常自带独立tnsnames.ora,容易漏改;Java 应用若用 JDBC URL 直连(如jdbc:oracle:thin:@//host:1521/svc),则根本不用 TNS,需单独处理
客户端连接池与连接泄漏风险
很多应用用 HikariCP、Druid 等连接池,它们缓存的是物理连接,不是逻辑连接串。DG 切换后,池中已有连接仍指向旧库,会持续报错(如 ORA-12541: TNS:no listener),直到超时或被驱逐。
- 设置合理的
connection-test-query(如SELECT 1 FROM DUAL)和validation-timeout,让连接池主动探活 - 避免设过长的
max-lifetime(建议 ≤ 30 分钟),防止旧连接长期滞留 - 切换后手动触发连接池刷新(如 Druid 的
resetAPI),比等自动回收更可控
最麻烦的不是配错,而是以为配对了,结果连接池里一堆僵尸连接还在往旧地址发包——查 v$session 和网络抓包才能准确定位。











