Oracle ODP.NET 游标泄漏?
我使用以下代码遇到了打开游标限制问题。 oracle 数据库上的打开游标限制设置为 1000 左右。以下代码似乎保留了游标,即使我已经在 using
语句中获得了需要它的所有内容(我认为)。 (注意,我不需要从 outRefCursor2 中读取任何内容)
我是否缺少 using
或 ODP.net 的其他一些清理?
该异常始终出现在迭代 596 处。
static List<Thing> GetDetailsForItems(List<string> items) {
DateTime start = DateTime.UtcNow;
var things = new List<Thing>();
var spname = "SP_GET_THING_DETAILS";
var outRefCursorName1 = "p_ref_cursor1";
var outRefCursorName2 = "p_ref_cursor2";
// Create params
var pInput1 = new OracleParameter("p_input1",
OracleDbType.Varchar2, ParameterDirection.Input);
pInput1.Value = "";
// Input 2 can be blank
var pInput2 = new OracleParameter("p_input2",
OracleDbType.Varchar2, ParameterDirection.Input);
pInput2.Value = "";
var outRefCursor1 = new OracleParameter(outRefCursorName1,
OracleDbType.RefCursor, ParameterDirection.Output);
var outRefCursor2 = new OracleParameter(outRefCursorName2,
OracleDbType.RefCursor, ParameterDirection.Output);
int count = 0;
using (var conn = new OracleConnection(CONN_STR)) {
conn.Open();
using (var cmd = conn.CreateCommand()) {
cmd.Parameters.Add(pInput1);
cmd.Parameters.Add(pInput2);
cmd.Parameters.Add(outRefCursor1);
cmd.Parameters.Add(outRefCursor2);
cmd.CommandText = spname;
cmd.CommandType = CommandType.StoredProcedure;
foreach (string value in items) {
count++;
cmd.Parameters[pInput1.ParameterName].Value = value;
var execVal = cmd.ExecuteNonQuery();
using (var refCursor = (Types.OracleRefCursor)
cmd.Parameters[outRefCursorName1].Value) {
using (var reader = refCursor.GetDataReader()) {
while (reader.Read()) {
// read columns
things.Add(reader["COLUMN_A"].ToString());
}
} // close reader
} // close cursor
} // end foreach
} // close command
} // close connection
int seconds = (DateTime.UtcNow - start).Seconds;
Console.WriteLine("Finished in {0} seconds", seconds);
return things;
}
我正在使用在线找到的此代码片段来监视数据库游标。我可以在单步执行代码时观察光标的累加。他们只是不断在 cmd.ExecuteNonQuery()
行添加。在任何 using 语句关闭后我从未看到任何下降。
select sum(a.value) total_cur, avg(a.value) avg_cur, max(a.value) max_cur,
s.username, s.machine
from v$sesstat a, v$statname b, v$session s
where a.statistic# = b.statistic# and s.sid=a.sid
and b.name = 'opened cursors current'
and machine='MY COMPUTER'
group by s.username, s.machine
order by 1 desc;
I'm running into an open cursor limit issue with using the following code. The open cursor limit on the oracle db is set to around 1000. The following code seems to hold onto the cursors even though I've got everything in a using
statement (I think) that requires it. (Note, I don't need to read anything from outRefCursor2)
Am I missing a using
or some other clean up with ODP.net?
The exception occurs consistently at iteration 596.
static List<Thing> GetDetailsForItems(List<string> items) {
DateTime start = DateTime.UtcNow;
var things = new List<Thing>();
var spname = "SP_GET_THING_DETAILS";
var outRefCursorName1 = "p_ref_cursor1";
var outRefCursorName2 = "p_ref_cursor2";
// Create params
var pInput1 = new OracleParameter("p_input1",
OracleDbType.Varchar2, ParameterDirection.Input);
pInput1.Value = "";
// Input 2 can be blank
var pInput2 = new OracleParameter("p_input2",
OracleDbType.Varchar2, ParameterDirection.Input);
pInput2.Value = "";
var outRefCursor1 = new OracleParameter(outRefCursorName1,
OracleDbType.RefCursor, ParameterDirection.Output);
var outRefCursor2 = new OracleParameter(outRefCursorName2,
OracleDbType.RefCursor, ParameterDirection.Output);
int count = 0;
using (var conn = new OracleConnection(CONN_STR)) {
conn.Open();
using (var cmd = conn.CreateCommand()) {
cmd.Parameters.Add(pInput1);
cmd.Parameters.Add(pInput2);
cmd.Parameters.Add(outRefCursor1);
cmd.Parameters.Add(outRefCursor2);
cmd.CommandText = spname;
cmd.CommandType = CommandType.StoredProcedure;
foreach (string value in items) {
count++;
cmd.Parameters[pInput1.ParameterName].Value = value;
var execVal = cmd.ExecuteNonQuery();
using (var refCursor = (Types.OracleRefCursor)
cmd.Parameters[outRefCursorName1].Value) {
using (var reader = refCursor.GetDataReader()) {
while (reader.Read()) {
// read columns
things.Add(reader["COLUMN_A"].ToString());
}
} // close reader
} // close cursor
} // end foreach
} // close command
} // close connection
int seconds = (DateTime.UtcNow - start).Seconds;
Console.WriteLine("Finished in {0} seconds", seconds);
return things;
}
I'm using this snippet found online to monitor DB cursors. I can watch the cursors add up while stepping through the code. And they just keep adding at the cmd.ExecuteNonQuery()
line. I never see a drop after any using statement closes.
select sum(a.value) total_cur, avg(a.value) avg_cur, max(a.value) max_cur,
s.username, s.machine
from v$sesstat a, v$statname b, v$session s
where a.statistic# = b.statistic# and s.sid=a.sid
and b.name = 'opened cursors current'
and machine='MY COMPUTER'
group by s.username, s.machine
order by 1 desc;
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
即使您没有使用
outRefCursor2
,您仍然需要提取它并在它返回有效游标时关闭它。 ODP.net 不会像 .Net 版本那样很好地处理资源,因此您需要确保处理 ODP.net 命令返回的所有内容。作为额外的步骤,在游标上显式调用.Close()
可能不会有什么坏处,以确保您实际上正在关闭它们(尽管 dispose 应该处理这一点)。Even though you're not using
outRefCursor2
you still need to extract it and close it if it returns a valid cursor. ODP.net doesn't dispose of resources as nicely as the .Net version did so you need to make sure you dispose everything that is returned by ODP.net commands. As an extra step, it may not hurt to go and call.Close()
explicitly on the cursors either to ensure that you're actually closing them (though the dispose should take care of that).您需要处置参数:
我更喜欢在连接的生命周期内处置参数,以防止任何当引用游标需要/想要使用处置连接时出现问题(可能是迷信)
静态列表GetDetailsForItems(列表项)
{
日期时间开始 = DateTime.UtcNow;
var things = new List();
var spname = "SP_GET_THING_DETAILS";
var outRefCursorName1 = "p_ref_cursor1";
var outRefCursorName2 = "p_ref_cursor2";
<前><代码>尝试
{
整数计数=0;
使用 (var conn = new OracleConnection(CONN_STR))
尝试
{
conn.Open();
// 创建参数
var pInput1 = new OracleParameter("p_input1", OracleDbType.Varchar2, ParameterDirection.Input);
pInput1.Value = "";
// 输入2可以为空
var pInput2 = new OracleParameter("p_input2", OracleDbType.Varchar2, ParameterDirection.Input);
pInput2.Value = "";
var outRefCursor1 = new OracleParameter(outRefCursorName1, OracleDbType.RefCursor, ParameterDirection.Output);
var outRefCursor2 = new OracleParameter(outRefCursorName2, OracleDbType.RefCursor, ParameterDirection.Output);
使用 (var cmd = conn.CreateCommand())
{
cmd.Parameters.Add(pInput1);
cmd.Parameters.Add(pInput2);
cmd.Parameters.Add(outRefCursor1);
cmd.Parameters.Add(outRefCursor2);
cmd.CommandText = spname;
cmd.CommandType = CommandType.StoredProcedure;
foreach(项目中的字符串值)
{
计数++;
cmd.Parameters[pInput1.ParameterName].Value = 值;
var execVal = cmd.ExecuteNonQuery();
使用 (var refCursor = (Types.OracleRefCursor)
cmd.Parameters[outRefCursorName1].Value)
{
使用 (var reader = refCursor.GetDataReader())
{
while (读者.Read())
{
// 读取列
things.Add(reader["COLUMN_A"].ToString());
}
} // 仔细阅读
} // 关闭光标
} // 结束 foreach
} // 关闭命令
} // 关闭连接
最后
{
pInput1.Dispose();
pInput2.Dispose();
outRefCursorName1.Dispose();
outRefCursorName2.Dispose();
}
}
int 秒 = (DateTime.UtcNow - 开始).Seconds;
Console.WriteLine("在 {0} 秒内完成", 秒);
归还东西;
}
You need to dispose of the parameters:
I prefer to dispose of the params within the life-time of the connection so as to prevent any issues when the ref cursors need/want to use the connection on dispose (superstition, probably)
static List GetDetailsForItems(List items)
{
DateTime start = DateTime.UtcNow;
var things = new List();
var spname = "SP_GET_THING_DETAILS";
var outRefCursorName1 = "p_ref_cursor1";
var outRefCursorName2 = "p_ref_cursor2";
}
我不会选择 GC.collect()...这是一种矫枉过正...http://blogs.msdn.com/b/scottholden/archive/2004/12/28/339733.aspx
但是当然,处理命令对象对我有用。简单的是使用“使用”,
如下所示:
I wouldn't go for GC.collect()... It is an overkill...http://blogs.msdn.com/b/scottholden/archive/2004/12/28/339733.aspx
But making sure disposing the command object worked for me. Easy is to use the "Using"
Something like this:
到目前为止,这些建议都没有奏效。所以无奈之下,我最终只能每 200 次迭代强制进行一次 GC 收集。用下面的代码。
奇怪的是,当从单元测试中调用此方法时,手动 GC.Collect() 不会释放任何游标。但是当从业务层调用该方法时,它确实起作用了,我可以通过监视oracle DB看到打开的游标被释放。
None of the suggestions had worked thus far. So in desperation, I ended up force GC collection every 200 iterations. With the following code.
What's strange is that when calling this method from a unit test, the manual
GC.Collect()
does not release any cursors. But when calling the method from the business layer, it actually does work and I can see the open cursors get released by monitoring the oracle DB.