-
数据库连接数的概念
强调此概念,是因为目前我们对数据库连接的概念不统一。
数据库连接是在TCP/IP、命名管道等通讯层之上,通过Oracle安全认证并关联数据库上下文信息的通讯通道。
在TCP/IP网络层之上的数据库连接,每打开一次,除了完成网络层的三次握手,还要进行数据库的安全认证以及数据库层上的上下文准备工作。所以建立一次数据库连接需要许多计算机和网络资源,而我们应该尽可以复用这些数据库连接。
数据库连接池完成这些数据库连接的管理,我们在使用时从数据库池中取出一个数据库连接,用完后马上告诉这个连接我已经用完,可以提供给其它人使用。如果一段时间内一直没有人使用数据库连接,数据库连接会关闭多余的空闲连接,避免资源的浪费。
-
先前的问题及处理方案
原来程序代码不规范等问题,造成许多数据库没有关闭。也就是没有通知数据库连接池不再使用此连接而让此连接可以再次复用。造成连接数据高不下。
通过检查代码,以及在代码中打桩方式,解决了上述问题。详见附件。
-
目前"伪连接数"情况说明
某某某此次提出的连接数,不是真正的数据库连接数,而是指的网络连接次数。日志(样本见附二)指示了哪些程序在何时建立了数据库连接。但是,并没有连接复用和连接关闭等方法的日志信息。
网络连接次数,是随着网站访问量的增加而增加的。如果不使用连接池,则网络连接次数和网站访问量成正比。通过连接池,可以在一定程序上利用现有连接,降低此网络连接次数。
-
改进监控方式
-
原理
Oracle数据库中有一个V$Session视图,可以实时体现当前数据库的每一个连接信息,如计算机名、进程名、是否活动、上次执行SQL的时间跨度等信息。通过固定周期的采样、分析、从而得到某一个计算机上的某进程名的数据库连接数以及活动连接数。
目前已经完成一个C#版的监视,每隔1分钟去读一次V$Session表,然后在程序中进行分析,记录,显示。由于VSession中的数据不会很多,且读取不分析,故对数据库的压力可以乎略。
-
监视程序评测
全部测试启动连接池。图示中,绿线代表连接池中的建立的连接,红线代表正在执行SQL语句的连接。
-
程序打开连接,执行SQL,关闭连接。通过连续调用50次, 最后退出程序。
从图中看出,连接池中使用只有一个连接,并采样时,此连接没有在执行SQL语句。由于连续调用50次。说明连接池中的连接一直在被复用。
-
程序打开连接,执行SQL,但不关闭连接。通过连续调用10次,最后关闭程序。
从图中看出,由于没有关闭连接,池中的已有非活动连接不能被复用。造成连接池中的连接持续增多。这些连接在程序关闭后自动被关闭。但如果程序不退出,这些连接会持续超过2000秒的时间后,才被断开、回收。
-
评测OA项目
-
关闭自动检测未关闭连接模块后,单用户连接测试,通过登陆OA4来连接进行一些操作。
上图是去掉了自动检测未闭关连接的HttpModule后的曲线,单用户连续处理几个文后,可以看到连接池中的数量持续上升。是函数:
WfMS.Engine.OperationLogic.ParticipantInstance.Enumerate(String, String, String, String)
没有正确关闭连接,也没有源代码,从而不能进行修正。
在对IIS进行重置后,即使用 iisreset命令后,看到连接数变成0。如果不重置
,约会在2000秒后自动关闭,取决于Dotnet的垃圾回收。如下图所示-
启用自动检测未关闭连接模块后,单用户连接测试,通过登陆OA4来连接进行一些操作。
进行连接处理公文等操作后,可以看到连接池中只有两个连接(一个是前端表连接,一个是引擎表连接),这两个连接,在多次的请求中被复用。
如上图所示:由于正确关闭连接,连接池会在60秒时自动回收,关闭连接。说明自动检测模块起到了它应有的作用。
-
由测试人员协助,并发20测试,隔一段时间,并发50测试。
从上图得出,随着并发量的增加,数据库连接量随着增加,但数据中活动连接的数量基本为零,数据连接在并发时,不能复用。
由于OA4的数据库连接正确关闭,所以经过约60秒的时间,连接池自动关闭这些空闲的连接。
-
4用户并发,连接测试一分钟
-
-
-
从上图得出,4个用户并发测试一分钟,期间请求多次,并没有造成连接池中连接累积,连接池中的连接在多次的请求得到了复用。
由于OA4的数据库连接正确关闭,所以经过约60秒的时间,连接池自动关闭这些空闲的连接。
-
总结
从上面的测试得出,数据库连接监视工具得么的数据分析可以反应实际情况和问题。
OA4的代码经过优化及自动关闭连接模块的协助,没有发现未关闭的数据库连接,上次处理方案达到预定效果。
信息技术部 产品中心
牛昆亮
二○○九年四月三十日
附一:
数据库连接检测代码
实 现 原 理
原代码结构
OA4主要分为业务和逻辑层,其中数据库访问的代码全部集中在逻辑层。逻辑层数据访问类共有两个:DBAccess.BaseLib.DBOperator,DBDataOperator.DBOperator。几乎所有的数据库连,都是通过这两个类中的打开连接和关闭连接的函数来实现。
出现未关闭连接的原因
在上层对象的数据库访问代码中,都是显式打开数据库连接,然后在进行完操作后,手动关闭连接。
数据访问类没有实现IDisposable接口,不能使用using语句来保证数据库连接被关闭。且代码中没有使用using的习惯。
有些代码在函数中,调用两次打开连接函数,每次打开时,都会创建新的数据库连接对象,原有的数据库连接不关闭。
解决方案
-
数据访问类实现 IDisposable接口
两个数据访问类实现该接口后,在代码所有使用地方,使用using语句,在函数退出时,由系统自动调用Dispose方法来关闭连接。
-
通过HttpModule技术,找到未关闭的连接,记录日志并关闭。
在数据访问类的打开连接函数中,检测系统函数调用堆栈,并把数据访问类的实例连同调用堆栈字串一共记录到线程槽中。在数据访问类的关闭连接函数中,从线程槽中去除该对象。
现在OA4期中未使用异步线程调用,故线程槽中的对象和调用逻辑层的代码具有一对一的关系。
HttpModule技术可以使用切片技术在每个WebService请求中,在HttpContext上进行事件监听注册。通过监视EndRequest(页面执行完事件),检查线程槽中数据库连接对象,如果有,便是有未关闭的连接,这时记录并关闭。
最后清除线程槽对象。
总结
在该方案中,对连接状态的检测,不涉及数据库操作。在打开数据时,获取函数调用上下文字串,也几乎不会占用CPU。故不会对数据库造成压力。
在该方案中,目前仍能找到许多引擎中没有关闭连接的代码,记录了大量的日志文件。如果在没有修正这些代码的时候去掉检测,这些连接不会关闭。如果修正这些代码,日志也不会再占用CPU资源。
OA组 牛某某
二○○九年四月二十日
附二:
某某某提供的日志样本
27-APR-2009 08:01:07 * (CONNECT_DATA=(SERVICE_NAME=oawf)(CID=(PROGRAM=E:\OAMP部署\监控和告警服务\OAMPSYS.exe)(HOST=WEBSERVICE)(USER=SYSTEM))) * (ADDRESS=(PROTOCOL=tcp)(HOST=10.129.255.130)(PORT=4415)) * establish * oawf * 0
27-APR-2009 08:01:20 * (CONNECT_DATA=(SID=OAWF)(SERVER=DEDICATED)(CID=(PROGRAM=c:\windows\system32\inetsrv\w3wp.exe)(HOST=S72)(USER=NETWORK?SERVICE))) * (ADDRESS=(PROTOCOL=tcp)(HOST=10.129.255.84)(PORT=3501)) * establish * OAWF * 0
27-APR-2009 08:01:57 * (CONNECT_DATA=(SERVICE_NAME=OAWF)(CID=(PROGRAM=c:\program files\默认公司名称\wfenginsetup\wfengineservice.exe)(HOST=S73)(USER=OAWebUser))) * (ADDRESS=(PROTOCOL=tcp)(HOST=10.129.255.81)(PORT=3236)) * establish * OAWF * 0
27-APR-2009 08:02:21 * (CONNECT_DATA=(SERVICE_NAME=OAWF)(CID=(PROGRAM=c:\program files\默认公司名称\wfenginsetup\wfengineservice.exe)(HOST=S73)(USER=OAWebUser))) * (ADDRESS=(PROTOCOL=tcp)(HOST=10.129.255.81)(PORT=3241)) * establish * OAWF * 0
27-APR-2009 08:02:21 * (CONNECT_DATA=(SERVICE_NAME=OAWF)(CID=(PROGRAM=c:\program files\默认公司名称\wfenginsetup\wfengineservice.exe)(HOST=S73)(USER=OAWebUser))) * (ADDRESS=(PROTOCOL=tcp)(HOST=10.129.255.81)(PORT=3242)) * establish * OAWF * 0
27-APR-2009 08:02:47 * 12502
27-APR-2009 08:02:47 * (CONNECT_DATA=(SERVICE_NAME=oawf)(CID=(PROGRAM=E:\OAMP部署\监控和告警服务\OAMPSYS.exe)(HOST=WEBSERVICE)(USER=SYSTEM))) * (ADDRESS=(PROTOCOL=tcp)(HOST=10.129.255.130)(PORT=4006)) * establish * oawf * 0
27-APR-2009 08:02:48 * (CONNECT_DATA=(SERVICE_NAME=oawf)(CID=(PROGRAM=c:\windows\system32\inetsrv\w3wp.exe)(HOST=WEBS13)(USER=ASPNET))) * (ADDRESS=(PROTOCOL=tcp)(HOST=10.129.255.65)(PORT=4089)) * establish * oawf * 0
27-APR-2009 08:02:53 * (CONNECT_DATA=(SERVICE_NAME=oawf)(CID=(PROGRAM=c:\windows\system32\inetsrv\w3wp.exe)(HOST=WEBS13)(USER=ASPNET))) * (ADDRESS=(PROTOCOL=tcp)(HOST=10.129.255.65)(PORT=4090)) * establish * oawf * 0
27-APR-2009 08:03:19 * (CONNECT_DATA=(SERVICE_NAME=oawf)(CID=(PROGRAM=c:\windows\system32\inetsrv\w3wp.exe)(HOST=WEBS15)(USER=NETWORK?SERVICE))) * (ADDRESS=(PROTOCOL=tcp)(HOST=10.129.255.67)(PORT=4073)) * establish * oawf * 0
27-APR-2009 08:03:32 * (CONNECT_DATA=(SERVICE_NAME=oawf)(CID=(PROGRAM=c:\windows\system32\inetsrv\w3wp.exe)(HOST=WEBS15)(USER=NETWORK?SERVICE))) * (ADDRESS=(PROTOCOL=tcp)(HOST=10.129.255.67)(PORT=4074)) * establish * oawf * 0
27-APR-2009 08:04:22 * (CONNECT_DATA=(SERVICE_NAME=OAWF)(CID=(PROGRAM=c:\program files\默认公司名称\wfenginsetup\wfengineservice.exe)(HOST=S73)(USER=OAWebUser))) * (ADDRESS=(PROTOCOL=tcp)(HOST=10.129.255.81)(PORT=3255)) * establish * OAWF * 0