最近一直和ORA-12516报错作斗争。接着我之前分享的一篇文章说起 ,当时通过kill进程的方法临时解决了问题,但是根本问题没有找到——为什么设备会产生大量连接?说到底,这个问题当时并没有解决,并且继续“折磨”了我将近一个月。这段时间,不断地有人找我——数据库连不上了!!!问题到底出在哪儿了呢。
下面和大家分享一下解决过程,希望对大家有所借鉴。
开发环境
操作系统:Windows Server 2008 R2 Standard
数据库:Oracle 10g(10.2.0.4) 64bit
频繁报错ORA-12516,初步分析
最近ORA-12516错误频出,我也是不胜其烦。但是每次都找不到根本原因,只能停监听、杀进程或者直接关闭客户端程序。
复述一下故障现象:连接数据库报错,提示“ORA-12516”错误——“ORA-12516: TNS: 监听程序无法找到匹配协议栈的可用句柄”。
一直很稳定的系统架构,为什么突然不行了呢?
数据库processes参数使用的默认值150,相应的sessions为170,;
我们采用C/S架构,其中Client数量并不多,也就20台左右,所以正常情况下sessions数量为40左右,其中包括16个数据库内部连接。
可现在,sessions数量动辄就到了130~140,直接导致ORA-12516报错。
下面贴一个当时的查询结果:
SQL> set pagesize 150
SQL> set linesize 200
SQL> col username for a10
SQL> col terminal for a20
SQL> col program for a20
SQL> select username,program,terminal,count(*) from v$session group by rollup(username,program,terminal);
USERNAME PROGRAM TERMINAL COUNT(*)
---------- -------------------- -------------------- ----------
......此处省略内容
ORACLE.EXE (q000) 1
ORACLE.EXE (q001) DBSERVER 1
ORACLE.EXE (q001) 1
16
SYS sqlplus.exe DBSERVER 1
SYS sqlplus.exe 1
SYS 1
HOEGH HOEGH.exe Client7 1
HOEGH HOEGH.exe CLIENT2 95
HOEGH HOEGH.exe Client3 1
HOEGH HOEGH.exe Client4 2
HOEGH HOEGH.exe CLIENT5 28
HOEGH HOEGH.exe Client6 1
HOEGH HOEGH.exe 128
HOEGH 128
145
可以看出,CLIENT2 和CLIENT5 两个终端的连接数分别为95和28,肯定有问题。
查询session,尝试定位故障设备
通过select username,program,terminal from v$sessions;查询数据库连接,看到某一台或者某两台的session数达到30,有的甚至超过70。把相应设备的应用程序(应用程序连接数据库)重启后,问题有时能迅速缓解,有时却没有效果。
起初是怀疑Client设备的硬件或者操作系统,因为这些设备中的操作系统比较杂(实验室为了测试方便),包括32位的Windows XP、32位及64位的Win7,还有64位的Win8。
后来经过长时间的观察,发现故障设备并不固定,也就是说,每一台设备都有可能成为“故障设备”。
有点迷茫了。
C#清空连接池
经过长时间观察及初步分析,得出的结论是这个问题可能与具体设备没有直接关系,而是和目前的数据库断连判断代码有关。
我们的应用程序为了显示数据库连接状态,定时去连接数据库,起到“心跳”的作用。我们怀疑是不是每台设备都这么去频繁连接数据库,而这些连接却没有释放,时间长了session数量就上去了,于是在代码中添加了清空连接池的语句。
但是,没有效果。
其实,现在是怀疑一切了,因为以往项目我们采取的是同样的数据库“心跳”机制,一直没有出过问题。
添加静态监听
后来一直在网上查这个ORA-12516,不少朋友提到了通过监听去发现、解决问题。我看了一下数据库服务器的监听日志Listener.log,好家伙,都80多M了,记事本都打不开了。最后用UE打开监听日志,发现里面大片的TNS-12514报错。
10-6月 -2015 16:26:53 * service_update * hoegh * 0
10-6月 -2015 16:26:56 * (CONNECT_DATA=(CID=(PROGRAM=)(HOST=__jdbc__)(USER=))(SERVICE_NAME=hoegh)) * (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.3.191)(PORT=50641)) * establish * hoegh * 12514
TNS-12514: TNS: 监听程序当前无法识别连接描述符中请求的服务
10-6月 -2015 16:26:58 * (CONNECT_DATA=(CID=(PROGRAM=)(HOST=__jdbc__)(USER=))(SERVICE_NAME=hoegh)) * (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.3.191)(PORT=50644)) * establish * hoegh * 12514
TNS-12514: TNS: 监听程序当前无法识别连接描述符中请求的服务
10-6月 -2015 16:26:58 * (CONNECT_DATA=(CID=(PROGRAM=)(HOST=__jdbc__)(USER=))(SERVICE_NAME=hoegh)) * (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.3.191)(PORT=50645)) * establish * hoegh * 12514
TNS-12514: TNS: 监听程序当前无法识别连接描述符中请求的服务
10-6月 -2015 16:26:58 * (CONNECT_DATA=(CID=(PROGRAM=)(HOST=__jdbc__)(USER=))(SERVICE_NAME=hoegh)) * (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.3.191)(PORT=50647)) * establish * hoegh * 12514
TNS-12514: TNS: 监听程序当前无法识别连接描述符中请求的服务
10-6月 -2015 16:26:58 * (CONNECT_DATA=(CID=(PROGRAM=)(HOST=__jdbc__)(USER=))(SERVICE_NAME=hoegh)) * (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.3.191)(PORT=50648)) * establish * hoegh * 12514
TNS-12514: TNS: 监听程序当前无法识别连接描述符中请求的服务
10-6月 -2015 16:27:08 * service_update * hoegh * 0
10-6月 -2015 16:27:09 * (CONNECT_DATA=(SERVICE_NAME=hoegh)(CID=(PROGRAM=D:\HOEGHATS\HOEGH.exe)(HOST=)(USER=Administrator))) * (ADDRESS=(PROTOCOL=tcp)(HOST=3.16.11.50)(PORT=4528)) * establish * hoegh * 0
根据大家的建议,我尝试在监听配置文件Listener.ora中添加了静态监听,其中黄色部分为新添加内容。
但是,效果不大。
SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(SID_NAME = PLSExtProc)
(ORACLE_HOME = c:\oracle\product\10.2.0\db_1)
(PROGRAM = extproc)
)
(SID_DESC =
(SID_NAME = hoegh)
(ORACLE_HOME = c:\oracle\product\10.2.0\db_1)
)
)
LISTENER =
(DESCRIPTION_LIST =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = DBSERVER)(PORT = 1521))
(ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC0))
)
)
增大processes初始化参数
黔驴技穷,我又使了这招,可是丝毫不起作用,不同的是以往连接总数是一百三四十,现在增加到了二百七八十,问题依旧。
结合上面的静态监听,我再次查看listener.log,发现大量TNS-12518和TNS-12560报错,以及少量的TNS-12514报错:
15-6月 -2015 15:39:13 * 12502
TNS-12502: TNS: 监听程序没有从客户机收到 CONNECT_DATA
15-6月 -2015 15:39:13 * (CONNECT_DATA=(SERVICE_NAME=hoegh)(CID=(PROGRAM=D:\HOEGHATS\HOEGH.exe)(HOST=CLIENT5)(USER=work))) * (ADDRESS=(PROTOCOL=tcp)(HOST=3.16.210.94)(PORT=55359)) * establish * hoegh * 12518
TNS-12518: TNS: 监听程序无法分发客户机连接
TNS-12560: TNS: 协议适配器错误
15-6月 -2015 15:39:13 * (CONNECT_DATA=(SERVICE_NAME=hoegh)(CID=(PROGRAM=D:\HOEGHATS\HOEGH.exe)(HOST=)(USER=JDWL))) * (ADDRESS=(PROTOCOL=tcp)(HOST=3.20.16.50)(PORT=2200)) * establish * hoegh * 12518
TNS-12518: TNS: 监听程序无法分发客户机连接
TNS-12560: TNS: 协议适配器错误
15-6月 -2015 15:39:13 * (CONNECT_DATA=(SERVICE_NAME=hoegh)(CID=(PROGRAM=D:\HOEGHATS\HOEGH.exe)(HOST=DISPATCHER1)(USER=work))) * (ADDRESS=(PROTOCOL=tcp)(HOST=3.16.210.92)(PORT=50761)) * establish * hoegh * 12518
TNS-12518: TNS: 监听程序无法分发客户机连接
TNS-12560: TNS: 协议适配器错误
15-6月 -2015 15:39:13 * 12502
TNS-12502: TNS: 监听程序没有从客户机收到 CONNECT_DATA
15-6月 -2015 15:39:13 * (CONNECT_DATA=(SERVICE_NAME=hoegh)(CID=(PROGRAM=D:\HOEGHATS\HOEGH.exe)(HOST=WINOW-BWMCG6U3V)(USER=JDWL))) * (ADDRESS=(PROTOCOL=tcp)(HOST=3.20.1.52)(PORT=3487)) * establish * hoegh * 12518
TNS-12518: TNS: 监听程序无法分发客户机连接
TNS-12560: TNS: 协议适配器错误
15-6月 -2015 15:39:13 * (CONNECT_DATA=(SERVICE_NAME=hoegh)(CID=(PROGRAM=D:\HOEGHATS\HOEGH.exe)(HOST=ATSSERVER1)(USER=Administrator))) * (ADDRESS=(PROTOCOL=tcp)(HOST=3.16.210.74)(PORT=3404)) * establish * hoegh * 12518
TNS-12518: TNS: 监听程序无法分发客户机连接
TNS-12560: TNS: 协议适配器错误
15-6月 -2015 15:39:13 * (CONNECT_DATA=(SERVICE_NAME=hoegh)(CID=(PROGRAM=D:\HOEGHATS\HOEGH.exe)(HOST=CLIENT2)(USER=Administrator))) * (ADDRESS=(PROTOCOL=tcp)(HOST=3.16.210.66)(PORT=2229)) * establish * hoegh * 12518
TNS-12518: TNS: 监听程序无法分发客户机连接
TNS-12560: TNS: 协议适配器错误
重建监听器
重启设备是一个“大招”,以前碰到“疑难杂症”重启往往能够收到奇效。最后,我甚至都想着重建数据库了,不过考虑着动静有点大,可能会影响其他人工作,只好作罢;那我重建一下监听试一下吧。
于是,我通过netca删除原有监听,重建监听程序listener,都采用默认配置。
可是,问题依旧。
更换微软驱动System.Data.OracleClient
真是没招了,最后和部门的架构师一起讨论这个问题,会不会和数据库驱动有关系呢。
我们的应用程序是用C#开发的,连接数据库需要数据驱动支持,以前我们采用的是微软的数据库驱动System.Data.OracleClient,最近这个项目换成了Oracle官方驱动Oracle.ManagedDataAccess.Client。至于为什么要换,最主要是考虑Oracle官方驱动部署方便,无需安装Oracle数据库客户端。
说干就干,把实验室的所有客户端设备的驱动都更换为System.Data.OracleClient,我再去查看session连接状况,看到输出结果哭了都快,session数真的好少啊!!!
SQL> set pagesize 150
SQL> set linesize 200
SQL> col username for a10
SQL> col terminal for a20
SQL> col program for a20
SQL> select username,program,terminal,count(*) from v$session group by rollup(username,program,terminal);
USERNAME PROGRAM TERMINAL COUNT(*)
---------- -------------------- -------------------- ----------
......此处省略内容
ORACLE.EXE (q000) 1
ORACLE.EXE (q001) DBSERVER 1
ORACLE.EXE (q001) 1
16
SYS sqlplus.exe DBSERVER 1
SYS sqlplus.exe 1
SYS 1
HOEGH HOEGH.exe Client7 1
HOEGH HOEGH.exe CLIENT2 1
HOEGH HOEGH.exe Client3 1
HOEGH HOEGH.exe Client4 2
HOEGH HOEGH.exe CLIENT5 1
HOEGH HOEGH.exe Client6 1
HOEGH HOEGH.exe 7
HOEGH 7
24
说到最后的话
这个问题折腾一个多月了,现在看似得到彻底解决,但是心里还是不踏实,毕竟Oracle官方驱动Oracle.ManagedDataAccess.Client还是有不少优点的。
• Oracle驱动部署方便,数据类型兼容性好,如Timestamp类型支持保存ms级数据(大家可以参考以前 );
• 微软驱动更加稳定,但是不再提供更新,无法支持一些新特性。
那么,问题来了,会不会是我们的使用方法有问题呢,还请用过Oracle官方驱动Oracle.ManagedDataAccess.Client的朋友不吝赐教。
: