← Назад к вопросам

Что нагружается в базе данных, если соединение не закрыто и запросы не отправлены

1.0 Junior🔥 171 комментариев
#Soft Skills и карьера

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Проблемы с незакрытыми соединениями в базе данных

Это критическая проблема, которая приводит к полной неработоспособности приложения. Сегда можно использовать connection pooling.

Что происходит в БД

Незакрытое соединение удерживает ресурсы:

  1. Memory — каждое соединение требует памяти
  2. File descriptors — операционная система имеет лимит
  3. Lock на таблицы — открытая транзакция удерживает блокировки
  4. Сессия БД — требует системных ресурсов
// Антипаттерн: подключение не закрывается
public class BadDatabaseExample {
    public List<User> getAllUsers() {
        Connection connection = DriverManager.getConnection(url, user, password);
        Statement stmt = connection.createStatement();
        ResultSet rs = stmt.executeQuery("SELECT * FROM users");
        
        List<User> users = new ArrayList<>();
        while (rs.next()) {
            users.add(new User(rs.getString("name")));
        }
        // БД: соединение остаётся открытым!
        return users;
    }
}

Исчерпание ресурсов (Resource Exhaustion)

Что падает первым:

// 1. Connection Pool исчерпывается
// Pool max size: 20 соединений
// Открыто: 20 соединений (все занято)
// Новый запрос: ждёт 30 сек → timeout → ошибка

// 2. File descriptors заканчиваются
// Linux: ulimit -n (обычно 1024)
// После 1000 соединений: "Too many open files"

// 3. Память БД полностью заполняется
// shared_buffers, effective_cache_size
// БД становится медленной, затем падает

Симптомы проблемы

В логах приложения:

java.sql.SQLException: Cannot get a connection, pool max size is 20
    at com.zaxxer.hikari.HikariPool.throwPoolInitializationException
    at com.zaxxer.hikari.HikariPool.createConnection

ConnectionTimeoutException: Unable to acquire connection from pool

В БД (PostgreSQL):

-- Просмотр открытых соединений
SELECT count(*) FROM pg_stat_activity;
-- Результат: 500 соединений! (normal: 10-20)

-- Кто открыл эти соединения
SELECT pid, state, query FROM pg_stat_activity 
WHERE state != 'idle' LIMIT 20;

-- Убийство зависших соединений
SELECT pg_terminate_backend(pid) 
FROM pg_stat_activity 
WHERE state = 'idle' AND query_start < now() - interval '1 hour';

Правильное управление соединениями

Используем try-with-resources (Java 7+):

public class GoodDatabaseExample {
    public List<User> getAllUsers() {
        try (Connection connection = DriverManager.getConnection(url, user, password);
             Statement stmt = connection.createStatement();
             ResultSet rs = stmt.executeQuery("SELECT * FROM users")) {
            
            List<User> users = new ArrayList<>();
            while (rs.next()) {
                users.add(new User(rs.getString("name")));
            }
            // Автоматическое закрытие всех ресурсов
            return users;
        } catch (SQLException e) {
            throw new RuntimeException("Database error", e);
        }
    }
}

Используем Connection Pool (HikariCP в Spring Boot):

@Configuration
public class DataSourceConfig {
    
    @Bean
    public DataSource dataSource() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(dbUrl);
        config.setUsername(dbUser);
        config.setPassword(dbPassword);
        config.setMaximumPoolSize(20);      // Макс соединений
        config.setMinimumIdle(5);            // Мин соединений
        config.setConnectionTimeout(10000);  // Timeout 10 сек
        config.setIdleTimeout(600000);       // 10 мин idle
        config.setMaxLifetime(1800000);      // 30 мин жизни
        
        return new HikariDataSource(config);
    }
}

В Spring Data JPA:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    // Spring автоматически управляет соединениями
    @Query("SELECT u FROM User u WHERE u.name = ?1")
    User findByName(String name);  // Соединение закроется автоматически
}

Проблема с неотправленными запросами

Если запросы не отправлены:

// Антипаттерн: запрос подготовлен, но не выполнен
public void badExample() {
    try (Connection connection = getConnection()) {
        PreparedStatement stmt = connection.prepareStatement(
            "SELECT * FROM users WHERE id = ?"
        );
        stmt.setInt(1, 123);
        // stmt.executeQuery();  // ← Забыли вызвать!
        // Соединение ждёт, пока будет выполнена операция
    }
}

Что происходит:

  • Транзакция остаётся открытой
  • Блокировки не отпускаются
  • Другие операции ждут этой блокировки
  • Deadlock или timeout

Мониторинг и профилактика

Метрики для мониторинга:

@Configuration
public class MonitoringConfig {
    
    @Bean
    public MeterRegistry meterRegistry() {
        MeterRegistry registry = new SimpleMeterRegistry();
        
        // Количество активных соединений
        Gauge.builder("db.active.connections", 
            () -> dataSource.getHikariPoolMXBean().getActiveConnections())
            .register(registry);
        
        // Ожидающие в очереди
        Gauge.builder("db.pending.connections",
            () -> dataSource.getHikariPoolMXBean().getPendingThreads())
            .register(registry);
        
        return registry;
    }
}

Алерты:

ALERT: db.pending.connections > 5
  → Скоро будет exhaustion pool

ALERT: db.active.connections = max
  → Срочно нужно найти утечку

Поиск утечки соединений

// Профилирование соединений
try (Connection conn = dataSource.getConnection()) {
    System.out.println("Acquired connection: " + conn);
    // Используем соединение
} // Автоматически освобождается

Чтобы найти утечку:

  1. Включить DEBUG логирование в HikariCP
  2. Просмотреть stack traces открытых соединений
  3. Найти место, где забыли закрыть
  4. Исправить на try-with-resources