容灾切换后客户端连不上是因为新主库未注册所需service_name,须在OPEN状态下执行ALTER SYSTEM SET service_names及ALTER SYSTEM REGISTER;或用DBMS_SERVICE创建固定服务名并配触发器自动启停。

容灾切换后客户端连不上,不是网络不通、密码错或监听没启,而是新主库根本没注册你用的 service_name——它只默认注册了 db_unique_name 对应的服务。不手动补注册,重试十次也是 ORA-12516。
为什么 tnsnames.ora 加 RETRY_COUNT 没用
客户端重试机制只控制“单次连接尝试”的行为:超时多久、重试几次。但它每次连的还是那个不存在的服务名。监听器里压根没这个 service_name,lsnrctl status 里搜不到,再怎么 retry 都是重复报错。
-
CONNECT_TIMEOUT和RETRY_COUNT是客户端侧参数,解决不了服务注册缺失这个根本问题 - 真正要动的是数据库端:让新主库把需要的
service_names主动推给监听器 - 切完立刻查
lsnrctl status | grep -i "your_service",看不到就等于没生效
必须在切换脚本末尾加的两行命令
Switchover 或 Failover 完成后,新主库启动时不会自动继承原主库的 service_names 设置。哪怕 spfile 一模一样,它读的是自己控制文件里的 db_unique_name,注册的服务名默认就是它自己。
- 先设值:
ALTER SYSTEM SET service_names='myapp,scott' SCOPE=BOTH;(含空格或特殊字符需单引号) - 再强制推送:
ALTER SYSTEM REGISTER;(不是重启监听,是让 PMON 立即通知监听器) - 这两步缺一不可;漏掉
REGISTER,改了service_names也白搭
用 DBMS_SERVICE 实现透明切换(推荐用于读写分离场景)
如果希望客户端完全不感知切换,只认一个固定服务名(比如 myapp),就得靠数据库内建服务管理,而不是依赖静态配置。
- 在主库上创建并启动服务:
DBMS_SERVICE.CREATE_SERVICE('myapp','myapp')+DBMS_SERVICE.START_SERVICE('myapp') - 建触发器,确保每次
STARTUP后自动启停:AFTER STARTUP ON DATABASE触发器里判断v$database.database_role,是PRIMARY就启服务,否则停 - 备库无需任何操作——Data Guard 会同步触发器和
DBMS_SERVICE元数据 - 客户端 tnsnames.ora 只需指向一个含多个地址的
ADDRESS_LIST,靠服务名自动路由到当前主库
最容易被忽略的点:所有服务注册动作都必须在新主库处于 OPEN 状态下执行;ALTER SYSTEM REGISTER 不生效,往往是因为数据库刚启到 MOUNT 就急着跑命令。另外,逻辑备库的 DML 重定向(ADG_REDIRECT_DML)和这里的服务注册完全是两回事,别混用。


















