Как отслеживать утечки подключения к базе данных

У нас есть приложение, в котором, похоже, возникают утечки соединения (SQL Server сообщает, что достигнут максимальный размер пула). Я один на своей машине разработчика (очевидно), и просто перемещаясь по приложению, я вызываю эту ошибку. Монитор активности SQL Server показывает большое количество процессов, использующих мою базу данных.

Я хочу узнать, какие файлы открывают соединения, но не использую их. Я думал использовать что-то вроде grep, чтобы для каждого файла подсчитать количество «.Open ()» и количество «.Close ()» и получить файл, для которого числа не равны. Это реально?

Дополнительный вопрос: соответствуют ли процессы, обнаруженные в мониторе активности SQL Server, соединениям? Если нет, как мне узнать, сколько подключений открыто в моей базе данных?

Приложение находится в asp.net (vb) 3.5 с SQL Server 2005. В настоящее время мы не используем LINQ (пока) или что-то подобное.

Спасибо


person thomasb    schedule 21.04.2011    source источник
comment
на каком языке написано ваше приложение?   -  person Filip De Vos    schedule 21.04.2011
comment
Asp.net. Я отредактировал исходный вопрос, чтобы отразить это.   -  person thomasb    schedule 21.04.2011


Ответы (2)


При просмотре кода со стороны SQL Server вы можете выполнить следующий запрос, чтобы получить представление о том, какие запросы выполнялись в последний раз в спящих соединениях. (открытые соединения, которые ничего не делают)

SELECT ec.session_id, last_read, last_write, text, client_net_address, program_name, host_process_id, login_name
FROM sys.dm_exec_connections  ec
JOIN sys.dm_exec_sessions es
  ON ec.session_id = es.session_id
CROSS APPLY sys.dm_exec_sql_text(ec.most_recent_sql_handle) AS dest
where es.status = 'sleeping'

Со стороны приложения вы можете выполнять отладку с помощью sos.dll, как описано в следующих статьях:

Если вам нужна дополнительная информация о том, как использовать windbg, эти статьи станут хорошим вступлением:

person Filip De Vos    schedule 21.04.2011
comment
спасибо, но ваша ссылка на часть 2 такая же, как и на часть 1. Часть 2 здесь: blogs.msdn.com/b/psssql/archive/2009/02/10/ - person thomasb; 21.04.2011
comment
Думаю, я ничего не понимаю в этих статьях. Я даже не понимаю, где набирать команды. - person thomasb; 21.04.2011
comment
Извините, исправил ссылку на вторую часть. В статьях рассказывается об использовании windbg.exe и sos.dll для отладки вашего приложения. - person Filip De Vos; 21.04.2011

Лучший способ устранить утечки в соединениях - это сделать это во время тестирования.

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

@BeforeClass
public static void initConnectionLeakUtility() {
    if ( enableConnectionLeakDetection ) {
        connectionLeakUtil = new ConnectionLeakUtil();
    }
}
 
@AfterClass
public static void assertNoLeaks() {
    if ( enableConnectionLeakDetection ) {
        connectionLeakUtil.assertNoLeaks();
    }
}

ConnectionLeakUtil выглядит так:

public class ConnectionLeakUtil {
 
    private JdbcProperties jdbcProperties = JdbcProperties.INSTANCE;
 
    private List idleConnectionCounters = 
        Arrays.asList(
            H2IdleConnectionCounter.INSTANCE,
            OracleIdleConnectionCounter.INSTANCE,
            PostgreSQLIdleConnectionCounter.INSTANCE,
            MySQLIdleConnectionCounter.INSTANCE
    );
 
    private IdleConnectionCounter connectionCounter;
 
    private int connectionLeakCount;
 
    public ConnectionLeakUtil() {
        for ( IdleConnectionCounter connectionCounter : 
            idleConnectionCounters ) {
            if ( connectionCounter.appliesTo( 
                Dialect.getDialect().getClass() ) ) {
                this.connectionCounter = connectionCounter;
                break;
            }
        }
        if ( connectionCounter != null ) {
            connectionLeakCount = countConnectionLeaks();
        }
    }
 
    public void assertNoLeaks() {
        if ( connectionCounter != null ) {
            int currentConnectionLeakCount = countConnectionLeaks();
            int diff = currentConnectionLeakCount - connectionLeakCount;
            if ( diff > 0 ) {
                throw new ConnectionLeakException( 
                    String.format(
                        "%d connection(s) have been leaked! Previous leak count: %d, Current leak count: %d",
                        diff,
                        connectionLeakCount,
                        currentConnectionLeakCount
                    ) 
                );
            }
        }
    }
 
    private int countConnectionLeaks() {
        try ( Connection connection = newConnection() ) {
            return connectionCounter.count( connection );
        }
        catch ( SQLException e ) {
            throw new IllegalStateException( e );
        }
    }
 
    private Connection newConnection() {
        try {
            return DriverManager.getConnection(
                jdbcProperties.getUrl(),
                jdbcProperties.getUser(),
                jdbcProperties.getPassword()
            );
        }
        catch ( SQLException e ) {
            throw new IllegalStateException( e );
        }
    }
}

Реализации IdleConnectionCounter можно найти в этом [сообщении в блоге] [1], а версию MySQL можно найти так:

public class MySQLIdleConnectionCounter implements IdleConnectionCounter {
 
    public static final IdleConnectionCounter INSTANCE = 
        new MySQLIdleConnectionCounter();
 
    @Override
    public boolean appliesTo(Class<? extends Dialect> dialect) {
        return MySQL5Dialect.class.isAssignableFrom( dialect );
    }
 
    @Override
    public int count(Connection connection) {
        try ( Statement statement = connection.createStatement() ) {
            try ( ResultSet resultSet = statement.executeQuery(
                    "SHOW PROCESSLIST" ) ) {
                int count = 0;
                while ( resultSet.next() ) {
                    String state = resultSet.getString( "command" );
                    if ( "sleep".equalsIgnoreCase( state ) ) {
                        count++;
                    }
                }
                return count;
            }
        }
        catch ( SQLException e ) {
            throw new IllegalStateException( e );
        }
    }
}

Теперь, когда вы запустите свои тесты, вы получите ошибку при утечке соединения:

:hibernate-core:test
 
org.hibernate.jpa.test.EntityManagerFactoryClosedTest > classMethod FAILED
    org.hibernate.testing.jdbc.leak.ConnectionLeakException
person Vlad Mihalcea    schedule 12.07.2016