Oracle ODP.NET - утечка курсора? - PullRequest
       21

Oracle ODP.NET - утечка курсора?

4 голосов
/ 28 сентября 2011

Я столкнулся с проблемой ограничения открытого курсора с использованием следующего кода.Предел открытого курсора на базе данных 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;

Ответы [ 4 ]

6 голосов
/ 29 сентября 2011

Даже если вы не используете outRefCursor2, вам все равно нужно извлечь его и закрыть, если он возвращает действительный курсор.ODP.net распределяет ресурсы не так хорошо, как в версии .Net, поэтому вам необходимо убедиться, что вы утилизируете все , возвращаемое командами ODP.net.В качестве дополнительного шага может не повредить пойти и явно вызвать .Close() на курсорах, чтобы убедиться, что вы фактически закрываете их (хотя утилита должна об этом позаботиться).

3 голосов
/ 29 сентября 2011

Вам нужно избавиться от параметров:

  • да, я подтвердил это перед показом утечек собственных ресурсов (виртуальный рабочий набор), когда мы не
  • Я предпочитаючтобы избавиться от параметров в течение времени жизни соединения, чтобы предотвратить любые проблемы, когда рефлекторы должны / хотят использовать соединение при удалении (суеверие, вероятно)

    статический список GetDetailsForItems (элементы списка){DateTime start = DateTime.UtcNow;var вещи = новый список ();var spname = "SP_GET_THING_DETAILS";var outRefCursorName1 = "p_ref_cursor1";var outRefCursorName2 = "p_ref_cursor2";

    try
    {
        int count = 0;
        using (var conn = new OracleConnection(CONN_STR))
        try
        {
            conn.Open();
            // 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);
    
            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
        finally
        {
    
            pInput1.Dispose();
            pInput2.Dispose();
            outRefCursorName1.Dispose();
            outRefCursorName2.Dispose();
        }
    }
    int seconds = (DateTime.UtcNow - start).Seconds;
    Console.WriteLine("Finished in {0} seconds", seconds);
    return things;
    

    }

1 голос
/ 15 марта 2013

Я бы не пошел на GC.collect () ... Это перебор ... http://blogs.msdn.com/b/scottholden/archive/2004/12/28/339733.aspx

Но убедившись, что избавление от командного объекта сработало для меня. Легко использовать «Использование»

Примерно так:

using(DbCommand command = dbConn1.CreateCommand())
{
    command.CommandText = sql;
    using (var dataReader = command.ExecuteReader())
    {
        dbRows = ToList(dataReader);
    }
    mvarLastSQLError = 0;
}
0 голосов
/ 17 октября 2011

Пока ни одно из предложений не сработало.Так что в отчаянии я в конечном итоге заставляю собирать GC каждые 200 итераций.Со следующим кодом:

if (count % 200 == 0) {
    GC.Collect();
}

Что странно, так это то, что при вызове этого метода из модульного теста руководство GC.Collect() не освобождает курсоры.Но при вызове метода из бизнес-уровня он действительно работает, и я вижу, как открытые курсоры освобождаются при мониторинге базы данных Oracle.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...