Oracle не удаляет курсоры после закрытия набора результатов - PullRequest
18 голосов
/ 01 апреля 2010

Примечание: мы повторно используем одно соединение.

************************************************
public Connection connection() {        
    try {
        if ((connection == null) || (connection.isClosed()))
        {
            if (connection!=null)
                log.severe("Connection was closed !");
            connection = DriverManager.getConnection(jdbcURL, username, password);
        }
    } catch (SQLException e) {
        log.severe("can't connect: " + e.getMessage());
    }
    return connection;        
}
**************************************************

public IngisObject[] select(String query, String idColumnName, String[] columns) {
    Connection con = connection();

    Vector<IngisObject> objects = new Vector<IngisObject>();
    try {
        Statement stmt = con.createStatement();

        String sql = query;
        ResultSet rs =stmt.executeQuery(sql);//oracle increases cursors count here
        while(rs.next()) {
            IngisObject o = new IngisObject("New Result");
            o.setIdColumnName(idColumnName);            
            o.setDatabase(this);
            for(String column: columns)
                o.attrs().put(column, rs.getObject(column));
            objects.add(o);
        }

        rs.close();// oracle don't decrease cursor count here, while it's expected
        stmt.close();
    } 
    catch (SQLException ex) {
        System.out.println(query);
        ex.printStackTrace();
    }

Ответы [ 4 ]

24 голосов
/ 06 апреля 2010

Параметр init.ora open_cursors определяет максимальное количество открытых курсоров, которое сеанс может иметь одновременно. Значение по умолчанию 50. Если приложение превышает это число, возникает ошибка «ORA-01000: превышено максимальное количество открытых курсоров».

Поэтому обязательно закрывать ресурсы JDBC, когда они больше не нужны, в частности java.sql.ResultSet и java.sql.Statement. Если они не закрыты, у приложения есть утечка ресурсов.

В случае повторного использования объекта Connection вы должны знать о том, что открытые курсоры оракула остаются открытыми и используются до тех пор, пока существует соединение и , транзакция не завершена. Когда приложение фиксируется, открытые курсоры освобождаются.

Поэтому, как разработчику приложения, вы должны знать приблизительную оценку необходимых открытых курсоров для вашей наиболее сложной транзакции.

Сложность заключается в неспособности внутренних представлений параметров оракула (v $ open_cursor, v $ sesstat и др.) Показать разницу между открытыми курсорами, которые можно использовать повторно, и открытыми курсорами, которые все еще заблокированы (не используются повторно). !) по незакрытому ResulSet или Заявлению. Если вы закроете все объекты Statement и ResultSet в своем блоке finally, ваше приложение будет в порядке.

Настройка параметра init.ora работает следующим образом (нашему приложению требуется максимум 800 курсоров)

ALTER SYSTEM SET open_cursors = 800 SCOPE=BOTH;
7 голосов
/ 01 апреля 2010

Обычно вы бы помещали операторы close для вашего ResultSet и Statement в блок finally, чтобы гарантировать, что они вызываются, даже если возникает исключение (может быть проблема, которая у вас возникла здесь). В вашем текущем коде, если возникает SQLException, тогда два вызова метода close () никогда не произойдут, а курсоры останутся открытыми.

Кроме того, какой запрос вы используете в Oracle для просмотра количества открытых курсоров?

Edit:
Этот код должен закрывать курсор. Если это не так, то вы сможете увидеть соотношение вызовов и методов при вызове вашего метода 1: 1, увеличивая на 1. Удостоверьтесь, что нет какого-то неожиданного процесса, вызывающего увеличение количества курсоров.

Если у вас есть права, вы можете выполнить этот запрос к базе данных, чтобы увидеть количество открытых курсоров по sid, чтобы увидеть, может быть, это какой-то другой процесс, который увеличивает курсоры, а не ваш конкретно. Он откроет любой с более чем 10 открытыми курсорами, вы можете увеличить его, чтобы отфильтровать шум или сузить его по имени пользователя или osuser:

select oc.sid,
       count(*) numCur,
       s.username username,
       s.osuser osuser,
       oc.sql_text,
       s.program
  from v$open_cursor oc,
       v$session s
 where s.sid = oc.sid
group by oc.sid, 
         oc.sql_text, 
         s.username, 
         s.osuser, 
         s.program
having count(*) > 10
order by oc.sid;

Еще один запрос, который может быть полезен в случае, если несколько sid используют одну и ту же строку запроса, поэтому вышеприведенное плохо раскрывает нарушителя:

 select oc.sql_text, count(*) 
   from v$open_cursor oc 
   group by oc.sql_text 
   having count(*) > 10 
   order by count(*) desc;
6 голосов
/ 19 июня 2012

У меня была та же проблема, и я обнаружил, что - если вы не закрываете соединение (потому что вы, возможно, будете использовать его позже), - вам, по крайней мере, нужно сделать connection.rollback () или connection.commit () для освобождения открытых курсоров вместе с закрытием ResultSet и операторов.

6 голосов
/ 01 апреля 2010

Правильный способ сделать это - закрыть каждый ресурс в блоке finally в своем собственном блоке try / catch. Я обычно использую статический служебный класс, подобный этому:

public class DatabaseUtils
{
    public static void close(Connection connection)
    {
        try
        {
            if (connection != null)
            {
                connection.close();
            }
        }
        catch (SQLException e)
        {
            // log exception here.
        }
    }

    // similar methods for ResultSet and Statement
}

Так что я бы написал ваш код так:

public IngisObject[] select(String query, String idColumnName, String[] columns) {

Vector<IngisObject> objects = new Vector<IngisObject>();

Connection con = null;
Statement stmt = null;
ResultSet rs = null;

try 
{
    connection = connection();
    stmt = con.createStatement();

    // This is a SQL injection attack waiting to happen; I'd recommend PreparedStatemen
    String sql = query;
    rs =stmt.executeQuery(sql);//oracle increases cursors count here
    while(rs.next()) 
    {
       IngisObject o = new IngisObject("New Result");
       o.setIdColumnName(idColumnName);            
       o.setDatabase(this);
       for(String column: columns) o.attrs().put(column, rs.getObject(column));
       objects.add(o);
    }

} 
catch (SQLException ex) 
{
    System.out.println(query);
    ex.printStackTrace();
}
finally
{
    DatabaseUtils.close(rs);
    DatabaseUtils.close(stmt);
    DatabaseUtils.close(con);
}
...