监听器注册与ORA-12514 错误分析

在Oracle data guard环境中,主库的alert.log文件出现"ORA-12514: TNS:listener does not currently know of service requested in connect descriptor"错误信息,导致主库上的日志无法传输到备库上,data guard数据同步不能实现。
这是一个最最基础的错误,在我们最初从客户端连接Oracle数据库时就可能碰到这个错误。
在主库上使用tnsping 这个oracle net服务别名,如下所示:

 

Attempting to contact (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.15.90)(PORT = 1844))) (CONNECT_DATA = (SERVICE_NAME = webdb)))
OK (0 msec)

 
据此信息可以判断,数据库服务器上的监听器正常,客户端与数据库服务器的网络通讯正常,请求的端口184正常。问题仅仅是监听器不识别在连接符中提供的服务。

 

(miki西游 @mikixiyou 原文链接: http://mikixiyou.iteye.com/blog/1718387 )

问题分析

 

主库上请求连接备库的服务名webdb,在备库上不能被监听器识别。
在备库上,服务名webdb既没有被监听器动态注册,也没有静态配置在监听器配置文件listener.ora中。
这里解释一下注册的概念。 
我个人理解,注册就是将数据库实例作为一个服务写到到监听程序的监控列表中。
客户端连接数据库时,可以通过这个服务名直接申请到对应的数据库连接,而不再通过数据库实例或者数据库名称。这样客户端也就可以不用知道数据库实例和数据库名称。虽然服务名一般和实例名是一样的,但有这个功能后就可以不一样。

在数据库实例启动过程中,数据库实例严格地说是PMON进程会向监听器注册相应的服务。这个过程称为动态注册。
监听器程序在服务注册后,就记录了服务名和实例名之间的对应关系。从而使得客户端使用服务名访问时能从服务名可以转换到实例名。一个服务名下可以包含多个不同的实例名称,如RAC的下服务名;同样,一个实例名下,可以有多个不同服务名。服务名和实例名的对应关系PMON进程从数据库初始化参数service_names和instances中获取。
在服务名注册到监听器之后,客户端只需要通过服务名就能访问某个实例。在RAC下,这个服务名可以用来实现负载均衡和透明故障切换。
如果这时客户端还是使用实例名访问数据库,那么就不能实现RAC下的负载均衡和故障透明切换。
但是,动态注册不一定时时都生效的。如ORA-12514就是动态注册不生效,导致监听器无法识别客户端连接符中提供的服务名,从而拒绝建立数据库连接时报的错误信息。
动态注册默认仅仅注册到默认的监听器上,这个默认值有三项指标:名称是LISTENER、端口是1521、协议是TCP。如果需要向非默认监听注册,则需要配置local_listener参数。
使用tnsping得到的信息显示,监听器使用的端口号是1844,不是默认选项。因此,该服务名并没有被自动注册进入到监听器。
将这个服务注册进入监听器的方法还有一种,称之为静态注册。
静态注册是监听器启动时读取listener.ora配置,将实例和服务注册到监听程序。
监听器中对应的实例无论是否启动,都能通过lsnrctl services查询得到,但状态永远都是UNKNOWN。
无论何时启动一个数据库,默认地都有两条信息注册到监听器中:数据库服务器对应的实例和服务。
我们采用静态注册方法将webdb服务注册到监听器中,$ORACLE_HOME/network/admin/listener.ora中的内容设置如下:

SID_LIST_LISTENER =
  (SID_LIST =
    (SID_DESC =
      (SID_NAME = PLSExtProc)
      (ORACLE_HOME = /u01/app/oratt/product/10.2.0/dbhome_1)
      (PROGRAM = extproc)
    )
    (SID_DESC =
      (SID_NAME = webdb)
      (ORACLE_HOME = /u01/app/oratt/product/10.2.0/dbhome_1)
    )
  )

LISTENER =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = serv1)(PORT = 1844))
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC0))
    )
  )

 
其中这几行文字是为了静态注册webdb而加入的,如下:

    (SID_DESC =
      (SID_NAME = webdb)
      (ORACLE_HOME = /u01/app/oratt/product/10.2.0/dbhome_1)
    )

 
监听器的服务状况一直是UNKNOWN,不管注册使用的数据库实例是否打开。如下所示:

oratt@serv1:/home/oratt=>webdb$lsnrctl services
LSNRCTL for Linux: Version 10.2.0.4.0 - Production on 07-NOV-2012 13:37:56
Copyright (c) 1991, 2007, Oracle.  All rights reserved.
Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=serv1)(PORT=1844)))
Services Summary...
Service "PLSExtProc" has 1 instance(s).
  Instance "PLSExtProc", status UNKNOWN, has 1 handler(s) for this service...
    Handler(s):
      "DEDICATED" established:0 refused:0
         LOCAL SERVER
Service "webdb" has 1 instance(s).
  Instance "webdb", status UNKNOWN, has 1 handler(s) for this service...
    Handler(s):
      "DEDICATED" established:3 refused:0
         LOCAL SERVER
The command completed successfully

 
因为这个监听器的端口号不是1521,所以采用了静态注册方法。
我们也可以不修改listener.ora文件,而采用配置local_listener参数的方法,将这个实例动态注册到监听器。
在数据库实例上修改local_listener参数,如下所示:
alter system set local_listener='(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.15.90)(PORT=1844))))';
修改之后,使用lsnrctl services检查监听器的服务状况,会发现有webdb注册进来,其状态是ready。发现该服务已经注册到监听器。

oratt@serv2:/home/oratt=>webdb$lsnrctl services
LSNRCTL for Linux: Version 10.2.0.4.0 - Production on 07-NOV-2012 13:55:38
Copyright (c) 1991, 2007, Oracle.  All rights reserved.
Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=serv2)(PORT=1844)))
Services Summary...
Service "PLSExtProc" has 1 instance(s).
  Instance "PLSExtProc", status UNKNOWN, has 1 handler(s) for this service...
    Handler(s):
      "DEDICATED" established:0 refused:0
         LOCAL SERVER
Service "webdb" has 1 instance(s).
  Instance "webdb", status READY, has 1 handler(s) for this service...
    Handler(s):
      "DEDICATED" established:0 refused:0 state:ready
         LOCAL SERVER
Service "webdb_XPT" has 1 instance(s).
  Instance "webdb", status READY, has 1 handler(s) for this service...
    Handler(s):
      "DEDICATED" established:0 refused:0 state:ready
         LOCAL SERVER
The command completed successfully

 

如果数据库实例不是OPEN状态,在lsnrctl services的结果中将没有webdb的服务。
在data guard环境下,即使使用local_listener做了动态注册设置,也不会生效的。因为备库实例根本就不是open状态。
从主库向备库传输日志是采用oracle net传输的,这样以来就带来比较隐蔽的ORA-12514错误,增加我们管理难度。
出现的错误信息如下类似:

Wed Nov  7 17:23:15 2012
Destination LOG_ARCHIVE_DEST_2 is UNSYNCHRONIZED
******************************************************************
LGWR: Setting 'active' archival for destination LOG_ARCHIVE_DEST_2
******************************************************************
LNSb started with pid=20, OS id=17139
Error 12514 received logging on to the standby
Wed Nov  7 17:23:22 2012
LGWR: Error 12514 creating archivelog file 'webdb_standby'
Wed Nov  7 17:23:22 2012
Errors in file /u01/app/oratt/admin/webdb/bdump/webdb_lgwr_11220.trc:
ORA-12514: TNS:listener does not currently know of service requested in connect descriptor
LGWR: Failed to archive log 3 thread 1 sequence 36 (12514)
Wed Nov  7 17:23:22 2012
Thread 1 advanced to log sequence 36 (LGWR switch)
  Current log# 3 seq# 36 mem# 0: /u01/app/oratt/webdb/redo03.log

 
在data guard的备库上,虽然我采用了动态注册,其实是不生效的。这点可以通过lsnrctl service去查证。只能采用静态注册,手工编辑listener.ora文件。


小结

 


通过对这个ORA-12514错误的分析和监听器工作原理一些介绍,以后在遇到这个错误时,我们应该先验证一下连接描述字符串中的service_name是不是不对,写错了还是服务器上监听器真的不能解析。
如果不能还不能解决,那么在客户端还有一种解决方法,就是将连接字符串中service_name修改为sid,直接从数据库实例进行连接。这是最传统的数据库访问方式。
使用service_name访问数据库的方式常常出现在Oracle RAC中,这是要使用它的负载均衡和透明故障切换功能。在很多JDBC数据库连接配置中,还是在坚持使用ip:sid:port方式连接数据库,这个方式的弊端经过此文你也了解一二了吧。

参考资料


以下是ORACLE官方文档对ORA-12514的描述:
ORA-12514: TNS:listener does not currently know of service requested in connect descriptor

Cause: The listener received a request to establish a connection to a database or
other service. The connect descriptor received by the listener specified a service
name for a service (usually a database service) that either has not yet dynamically
registered with the listener or has not been statically configured for the listener.
This may be a temporary condition such as after the listener has started, but before
the database instance has registered with the listener.

Action:
- Wait a moment and try to connect a second time.
- Check which services are currently known by the listener by executing: lsnrctl
services <listener name>
- Check that the SERVICE_NAME parameter in the connect descriptor of the net
service name used specifies a service known by the listener.
- If an easy connect naming connect identifier was used, check that the service
name specified is a service known by the listener.
- Check for an event in the listener.log file.

你可能感兴趣的:(oracle,listener,service_name,tns-12514)