文章目录
- ORA-12514:服务名未注册处理
- 报错现象
- 原因:动态注册是怎么工作的
- 排查步骤
- 第一步:看监听器上到底注册了什么
- 第二步:确认实例和参数
- 第三步:立即触发注册
- 第四步:注册目标对不上时,修 local_listener
- 第五步:主动追加服务名
- 兜底方案:静态注册
- 验证与预防
- 小结
ORA-12514:服务名未注册处理
报错现象
客户端连接被监听器拒绝:
C:\> sqlplus bizapp/Biz2024@orcl SQL*Plus: Release 19.0.0.0.0 - Production on 星期一 9月 28 10:20:11 2026 ERROR: ORA-12514: TNS:listener does not currently know of service requested in connect descriptor和 ORA-12541 的关键区别:监听器活着(否则报 12541),但它在自己的服务清单里找不到你请求的SERVICE_NAME = orcl。也就是说,问题不在"门开没开",而在"名单上没这个名字"。
原因:动态注册是怎么工作的
服务注册默认由数据库实例自动完成,叫动态注册。承担注册的进程随版本而异:11g 及以前是PMON,12c 起改为LREG(Listener Registration Process):
- 实例启动后,注册进程把本实例的
service_names注册给本机 1521 端口上的监听器;旧版本 PMON 约每60 秒轮询一次,12c 的 LREG 则在实例启动及服务配置变更时注册 - 监听器收到注册后,才把这个服务加进自己的服务清单,之后指向该服务的连接才能被受理
- 库关掉时注册进程消失,注册随之消失;监听器重启时清单清空,随后被请求重新注册
由此推出两个高发场景:
场景一:空窗期。机器重启后先起了监听器,数据库 startup 完成后注册可能尚未完成(旧版本 PMON 最多要等 60 秒)。这段窗口内连接全报 12514——库明明是好的。
场景二:注册目标对不上。监听器换了端口(不再是 1521),注册进程还往旧的 1521 注册,服务永远进不了监听器清单;或者库的service_names值和连接串里写的名字不一致(大小写、拼写、域名后缀)。
排查步骤
第一步:看监听器上到底注册了什么
lsnrctl services正常输出长这样:
正在连接到 (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=dbserver)(PORT=1521))) 服务 "orcl" 包含 1 个实例。 实例 "orcl", 状态 READY, 包含 1 个此服务的常规处理程序... 处理程序: "DEDICATED" 已建立:1 已被拒绝:0 状态:ready LOCAL SERVER对照三样东西:
- 服务名清单里有没有你连接串里
SERVICE_NAME的值(区分大小写) - 有的话实例状态是不是
READY(READY才能接业务连接) - 什么都没有——说明注册没发生,继续第二步
lsnrctl status的摘要部分也能看到服务摘要...,信息同理。
第二步:确认实例和参数
sqlplus / as sysdbaSQL> select instance_name, status from v$instance; INSTANCE_NAME STATUS ---------------- ------------ orcl OPEN SQL> show parameter service_names NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ service_names string orcl如果service_names的值与客户端连接串不一致(比如客户端写orcl,参数是orcl.example.com),改一边对齐即可。
第三步:立即触发注册
SQL> alter system register;这条命令让注册进程马上执行一轮注册。回头再看:
lsnrctl services服务出现了,客户端重连,通常就此解决。
第四步:注册目标对不上时,修 local_listener
如果监听器不在默认 1521 端口,注册进程默认只往 1521 注册,alter system register也白搭。查当前值:
SQL> show parameter local_listener修正为监听器实际地址(接第 10 篇改端口的场景):
SQL> alter system set local_listener='(ADDRESS=(PROTOCOL=TCP)(HOST=dbserver)(PORT=2521))' scope=both; SQL> alter system register;scope=both立即生效且写入 spfile;如果用的是 pfile 启动,改scope=memory并同步改 pfile 文件。
第五步:主动追加服务名
想让同一个实例多挂一个对外服务名(应用按服务名区分用途):
SQL> alter system set service_names='orcl,bizsvc' scope=both; SQL> alter system register;之后lsnrctl services里会多出bizsvc,客户端可以用SERVICE_NAME = bizsvc连接,落在同一个实例上。
兜底方案:静态注册
动态注册依赖"注册进程活着并完成注册",库没开、刚启动的空窗期、NOMOUNT/MOUNT 阶段都靠不住。想让监听器无条件认识某个 SID,用静态注册:在listener.ora里加SID_LIST_LISTENER:
SID_LIST_LISTENER = (SID_LIST = (SID_DESC = (GLOBAL_DBNAME = orcl) (ORACLE_HOME = /u01/app/oracle/product/19.0.0/dbhome_1) (SID_NAME = orcl) ) ) LISTENER = (DESCRIPTION_LIST = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = dbserver)(PORT = 1521)) ) )三个参数:
GLOBAL_DBNAME:对外提供的服务名,客户端SERVICE_NAME写这个值(或老式写法SID)ORACLE_HOME:实例的安装目录,路径必须真实存在SID_NAME:实例名,即ORACLE_SID
生效:
lsnrctl reload lsnrctl services静态注册的服务在输出里特征明显——状态是UNKNOWN:
服务 "orcl" 包含 1 个实例。 实例 "orcl", 状态 UNKNOWN, 包含 1 个此服务的常规处理程序...UNKNOWN 不代表错,只代表"监听器从配置文件背下了这个名字,不保证实例此刻可用"。连接打过来时监听器照样 spawn 一个 server process 去拉起会话,实例没开才会真正失败。
它的真实用途:
- 远程启动数据库:库是 nomount/idle 状态时,动态注册不存在,只有静态注册能让 DBA 从远程连上来执行 startup(云上运维没有本地终端时是刚需)
- 兜住重启后动态注册完成前的空窗
- 一些老版本 OEM / 第三方备份软件要求
局限:静态注册不感知实例真实状态(READY/UNKNOWN 之差),不带负载信息,RAC 环境下不能做服务漂移。动态注册为主、静态注册兜底,两者可以共存于同一个 listener.ora。
验证与预防
验证修复的标准动作,三连:
lsnrctl services# 服务在名单里,状态 READYsqlplus bizapp/Biz2024@192.168.1.10:1521/orcl# 直连串绕过 tnsnames 干扰sqlplus bizapp/Biz2024@orcl# 走 tnsnames 再验一遍预防:
- 开机脚本里,监听器启动之后、业务起来之前,加一句
echo "alter system register;" | sqlplus -S / as sysdba(或在 startup 脚本末尾执行),抹掉空窗期 - 改监听端口时同步改
local_listener(第 10 篇的完整步骤已含这一步) - 新建实例后核对
service_names与客户端连接串,写进交付文档 - 对空窗敏感、需要远程拉库的环境,配好
SID_LIST_LISTENER静态兜底 - 巡检时把
lsnrctl services的输出纳入例行检查项,服务数量和状态 READY 与历史基线对得上,注册类问题在爆发前就能看见
小结
- 12514 = 监听器活着,但服务清单里没有你要的名字;先
lsnrctl services看清单 - 动态注册由实例自动完成(旧版 PMON 约每 60 秒一轮),
alter system register;立即补一轮 - 注册目标靠
local_listener指路,非 1521 端口必查 SID_LIST_LISTENER静态注册是兜底:不依赖动态注册,代价是状态 UNKNOWN、不感知实例死活