我们有一个c# WCF web服务托管在Windows 2008 SP2/IIS 7访问Oracle数据库。通常数据访问工作正常,但在负载测试期间,它经常超时并记录和异常:
Error occurred when processing XXXXXXXX Web Service
Oracle.DataAccess.Client.OracleException Connection request timed out at Oracle.DataAccess.Client.OracleException.HandleErrorHelper(Int32 errCode, OracleConnection conn, IntPtr opsErrCtx, OpoSqlValCtx* pOpoSqlValCtx, Object src, String procedure, Boolean bCheck)
at Oracle.DataAccess.Client.OracleException.HandleError(Int32 errCode, OracleConnection conn, IntPtr opsErrCtx, Object src)
at Oracle.DataAccess.Client.OracleConnection.Open()
at MyWorkspace.WorkForceDataAccess.CheckStaffIdInRSW()
at MyWorkspace.MyClass.MyFunction(MyDataType MyData)
要查询数据库,我们使用如下命令:
OracleConnection orConn = new OracleConnection();
orConn.ConnectionString = "user id=xxx; password=xxx; Connection Timeout=600; Max Pool Size=150; data source= (DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = MYHOST.MYDOMAIN.com)(PORT = 1771)) (CONNECT_DATA =(SERVER = DEDICATED)(SERVICE_NAME = MYSERVICE.MYDOMAIN.com)))";
orConn.Open();
using (var cmd = new OracleCommand("MY_UTIL.check_StaffIdInRSW", orConn) { CommandType = CommandType.StoredProcedure })
{
cmd.Parameters.Add("P_Staff_Id", OracleDbType.Int32);
cmd.Parameters["P_Staff_Id"].Direction = ParameterDirection.Input;
cmd.Parameters["P_Staff_Id"].Value = Convert.ToInt32(MyDataObject.StaffId);
cmd.Parameters.Add("P_retvalue", OracleDbType.Int32);
cmd.Parameters["P_retvalue"].Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery(); // Execute the function
//obtain result
returnVal = int.Parse(cmd.Parameters["P_retvalue"].Value.ToString());
}
我非常确信正在调用的存储过程不会占用所有的时间。这是一个非常简单的过程,可以快速检查P_Staff_Id是否存在于表中并返回结果。
此外,这只发生在负载测试期间。在正常操作期间,情况很好,但在每秒1条消息的繁重负载期间,在平稳运行一段时间后会发生这种情况。
作为解决方案,我添加了"连接超时=600;最大池大小=150 "的连接字符串,但这并没有解决问题。
我们在开发服务器上运行相同的应用程序,它工作得很好。我们在那里从来没有遇到过这个问题。
如有任何建议,我将不胜感激。
我们有一个类似的问题,我们花了一些时间来调试和修复这个问题。我们的代码有很多输入文件,很多线程处理,每个线程使用实体框架和打开Oracle数据库连接,做一堆数据库查询和插入,偶尔使用文件。但大多数情况下都有效。
我修改了DbContext构造函数来显式地打开oraclecconnection。我添加了如下代码
for (i = 0; i < 5; i++)
try {
oracleConnection.Open();
} catch (OracleException) {
Sleep for 15 ms and retry.
On last attempt I also do OracleConnection.ClearAllPools()
}
它改进了,但仍然没有完全解决问题。我打破了从调试器捕获,并看到许多线程正在试图打开和少数线程正在处理。在Oracle堆栈中打开时,Oracle出于其内部目的执行ThreadPool。QueueUserWorkItem并等待其完成。我可以看到它在堆栈的顶部。这里有大量可用的池连接(默认为100),我几乎没有使用10。
但问题是在我们的代码中,我们也使用ThreadPool。QueueUserWorkItem,没有额外的限制。我认为把我们需要做的所有工作都排在队列中,让。net来处理这些事情是很酷的。但这有一个微妙的问题。我们所有的作业都消耗了全部队列数。当OracleConnection想要从池中获得一个池连接时,它也排队到线程池。但它永远不会完成。我们的工作都在等待oracleconconnection。打开,则其Queued Thread进程仍在队列中。最后,等待将通过超时退出。遗憾的是,即使有大量的连接池可用,我们已经消耗了所有的线程池进程,而Oracle的线程池甚至没有机会。这里设置ThreadPool。SetMaxThreads也不会有帮助。问题还是一样的。我们占用了所有的线程池资源,而oracle不会找到一个线程池,并且仍然在队列中。
修复不是只依赖ThreadPool,但是我们也添加了我们自己的节流。我使用了BlockingCollection和信号量,并在ThreadPool中只添加了一些限制数量的并发作业,比如5个。通过这种方式,oraclecconnection将始终找到可用的ThreadPool线程,并且不会失败。
即使我以前也经常遇到这个问题,即使在使用Connection.Close()
经过长时间的分析,我学到了以下几点
- connection . close()不处理数据库连接
- 连接超时并不意味着问题只存在于数据库查询
- 连接超时也可能是由于连接池中的耗尽连接(这对我来说是罪魁祸首,因为它达到了db连接的最大会话)
修复:- 分析花了很长时间,但修复只需2分钟
using(DbConnection instance)
{
}
如:-
using (DbConnection objDbConnection = new DbConnection())
{
ojDbConnection.PersistData();
}
下PersistData ();所有的数据库操作,如打开,关闭等。将被执行
众所周知"Using"是
的缩写形式try
{
}
catch()
{
}
Finally
{
Dispose objDbConnection;
}
希望它能有所帮助,就像它曾经帮助过我一样
尝试在末尾添加connection.close()。我没有看到在你的代码中释放连接并显式地将它们返回到连接池。因此,只有当GC启动时,连接才会返回到连接池。