Тонкости настройки HikariCP

Всем привет! В данной статье я постараюсь рассказать вам о интересных тонкостях работы с HikariPool. Мы разберем особенности настройки HikariCP для Java-приложений, обсудим потенциальные проблемы, которые могут возникнуть при работе с пулом подключений, и рассмотрим, как эффективно работать с двумя базами данных одновременно.

Made by DALLE

Made by DALLE

Что такое HikariPool?

HikariPool — это сверхбыстрый, легковесный пул подключений к базе данных, разработанный специально для Java-приложений. Его цель — обеспечить максимально эффективное использование ресурсов базы данных и минимизировать задержки при выполнении запросов. HikariCP, как его еще называют, славится своей производительностью и стабильностью, став одним из самых популярных пулов подключений в мире Java.

Почему же HikariCP так популярен? Во-первых, он минималистичен, но при этом невероятно мощный. В отличие от других пулов подключений, HikariCP не перегружен функционалом, который вы, скорее всего, никогда не будете использовать. Вместо этого он сосредоточен на главном — скорости и надежности. HikariCP оптимизирует процесс создания, управления и завершения подключений, что позволяет вашему приложению работать быстрее и эффективнее.

Кроме того, HikariCP легко настраивается. Благодаря множеству конфигурационных параметров, вы можете адаптировать пул подключений под свои нужды, обеспечив баланс между производительностью и стабильностью. Например, вы можете управлять максимальным количеством активных подключений, задавать таймауты ожидания и контролировать время жизни подключений, что особенно важно при работе с двумя базами данных одновременно.

7c1923bf4308c98928da91c710a80b6e.png

Настройка HikariCP

При работе с HikariPool важно понимать, что правильная настройка этого инструмента может значительно повлиять на производительность вашего Java-приложения. Давайте разберем основные параметры, которые следует настроит:

hikari:
  idle-timeout: 120000
  minimum-idle: 2
  maximum-pool-size: 30

1. idle-timeout

Параметр idle-timeout определяет, сколько миллисекунд неактивное подключение может находиться в пуле перед тем, как оно будет закрыто. В примере указано значение 120000, что эквивалентно 2 минутам. Это значит, что если подключение не используется в течение 2 минут, оно будет автоматически закрыто и освобождено.

2. minimum-idle

Параметр minimum-idle задает минимальное количество свободных (неактивных) подключений, которые пул всегда будет поддерживать. В нашем примере это значение равно 2. Это означает, что пул всегда будет стараться поддерживать хотя бы 2 свободных подключения, чтобы при необходимости они были сразу доступны для использования.

3. maximum-pool-size

Параметр maximum-pool-size определяет максимальное количество подключений, которые могут быть одновременно активны в пуле. В данном примере это значение равно 30. Этот параметр нужно тщательно настраивать в зависимости от требований вашего приложения и возможностей базы данных. Например, если вы используете PostgreSQL, где максимальное количество подключений установлено на уровне 500, значение maximum-pool-size должно быть настроено так, чтобы оставаться в пределах этого лимита, учитывая и другие приложения, использующие ту же базу данных.

Настройка HikariPool в зависимости от системы

Важно понимать, что значения этих параметров нужно выставлять самим, исходя из особенностей вашей системы и требований вашего приложения. Например, если ваше приложение имеет высокую нагрузку, возможно, потребуется увеличить maximum-pool-size, чтобы обеспечить достаточное количество подключений. Однако не стоит устанавливать его слишком высоким, чтобы не исчерпать лимиты подключений в базе данных и не создавать риски для других приложений, которые могут использовать те же ресурсы.

Возможные проблемы при неправильной настройке HikariPool

Если не настроить HikariPool должным образом, можно столкнуться с рядом проблем, которые могут серьезно повлиять на производительность вашего приложения. Одной из основных проблем является бесконтрольный рост количества подключений. Если maximum-pool-size не настроен, или установлен слишком высоким, количество подключений может начать расти бесконечно, не закрываясь своевременно. Это может привести к исчерпанию ресурсов базы данных, сбоям в работе других приложений и общему снижению производительности.

Кроме того, если не настроить параметры idle-timeout и minimum-idle, вы рискуете столкнуться с ситуацией, когда пул подключений будет переполнен неиспользуемыми, но не закрытыми подключениями, что также приведет к потере ресурсов и ухудшению отклика приложения.

536c7b7a066b1800ef9c8452d1c3374b.png

Настройка коннектов для нескольких баз

1. Определение атрибутов подключения к базам данных в application.yml

Первым шагом в настройке работы с несколькими базами данных в Spring является определение атрибутов подключения для каждой базы данных в конфигурационном файле application.yml. Здесь мы укажем параметры для двух баз данных: Zangetsu (PostgreSQL) и Zabimaru (Oracle).

Эти параметры включают URL подключения (jdbcUrl), имя пользователя (username), пароль (password), а также настройки пула подключений через HikariCP.

Пример конфигурации в application.yml:

spring:
  zangetsu:
    datasource:
      password: ZangetsuPass123!
      jdbcUrl: jdbc:postgresql://10.0.0.1:5432/zangetsu_db?currentSchema=zangetsu_schema
      username: zangetsu_user
      hikari:
        idle-timeout: 120000
        minimum-idle: 2
        maximum-pool-size: 15
      driver-class-name: org.postgresql.Driver
  zabimaru:
    datasource:
      jdbcUrl: "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=10.0.0.2)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=zabimaru_service)))"
      username: zabimaru_user
      password: ZabimaruPass#456
      driver-class-name: oracle.jdbc.OracleDriver
      schema: zabimaru_schema

2. Создание конфигурационных классов для каждой базы данных

После того как мы определили параметры подключения в application.yml, следующим шагом будет создание конфигурационных классов для каждой базы данных. В этих классах мы создадим бины для подключения к базе данных, EntityManager, TransactionManager и NamedParameterJdbcTemplate.

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

Также следует обратить внимание на правильное размещение Entity и репозиториев. Все классы Entity должны находиться в соответствующем пакете, указанном в конфигурации, чтобы Spring мог корректно настроить EntityManagerFactory. Репозитории должны быть в своих пакетах, чтобы Spring смог их обнаружить и связать с правильным EntityManagerFactory.

Конфигурация для базы данных Zangetsu (PostgreSQL)

В этом классе мы создаем конфигурацию для базы данных Zangetsu, которая использует PostgreSQL. Мы определяем бины для DataSource, EntityManagerFactory, TransactionManager и NamedParameterJdbcTemplate.

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        basePackages = "com.example.zangetsu.repository",
        entityManagerFactoryRef = "zangetsuEntityManagerFactory",
        transactionManagerRef = "zangetsuTransactionManager"
)
public class ZangetsuDatabaseConfig {

    @Bean(name = "zangetsuDataSource")
    @ConfigurationProperties(prefix = "spring.zangetsu.datasource.hikari")
    public DataSource zangetsuDataSource() {
        return dataSourceProperties().initializeDataSourceBuilder()
                .type(HikariDataSource.class).build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.omni.datasource")
    public DataSourceProperties dataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean(name = "zangetsuEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean zangetsuEntityManagerFactory(EntityManagerFactoryBuilder builder, @Qualifier("zangetsuDataSource") DataSource dataSource) {
        return builder
                .dataSource(dataSource)
                .packages("com.example.zangetsu.entity")
                .persistenceUnit("zangetsu")
                .build();
    }

    @Bean(name = "zangetsuTransactionManager")
    public PlatformTransactionManager zangetsuTransactionManager(@Qualifier("zangetsuEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }

    @Bean
    @Qualifier("zangetsuNamedParamJdbcTemplate")
    public NamedParameterJdbcTemplate zangetsuNamedParamJdbcTemplate(@Qualifier("zangetsuDataSource") DataSource dataSource) {
        return new NamedParameterJdbcTemplate(dataSource);
    }
}

Конфигурация для базы данных Zabimaru (Oracle)

Этот класс отвечает за конфигурацию для базы данных Zabimaru, которая использует Oracle. Мы создаем аналогичные бины для подключения к базе данных, но с уникальными именами для каждого метода.

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        basePackages = "com.example.zabimaru.repository",
        entityManagerFactoryRef = "zabimaruEntityManagerFactory",
        transactionManagerRef = "zabimaruTransactionManager"
)
public class ZabimaruDatabaseConfig {

    @Bean(name = "zabimaruDataSource")
    @ConfigurationProperties(prefix = "spring.zabimaru.datasource")
    public DataSource zabimaruDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "zabimaruEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean zabimaruEntityManagerFactory(EntityManagerFactoryBuilder builder, @Qualifier("zabimaruDataSource") DataSource dataSource) {
        return builder
                .dataSource(dataSource)
                .packages("com.example.zabimaru.entity")
                .persistenceUnit("zabimaru")
                .build();
    }

    @Bean(name = "zabimaruTransactionManager")
    public PlatformTransactionManager zabimaruTransactionManager(@Qualifier("zabimaruEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }

    @Bean
    @Qualifier("zabimaruNamedParamJdbcTemplate")
    public NamedParameterJdbcTemplate zabimaruNamedParamJdbcTemplate(@Qualifier("zabimaruDataSource") DataSource dataSource) {
        return new NamedParameterJdbcTemplate(dataSource);
    }
}

3. Настройка миграции с Flyway для каждой базы данных

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

Конфигурация Flyway для базы данных Zangetsu

В этом классе мы настраиваем миграции Flyway для базы данных Zangetsu. Здесь указывается путь к миграциям, специфичным для данной базы данных.

@Configuration
public class ZangetsuFlywayConfig {

    @Bean
    public Flyway zangetsuFlyway(@Qualifier("zangetsuDataSource") DataSource dataSource) {
        Flyway flyway = Flyway.configure()
                .dataSource(dataSource)
                .locations("classpath:db/migration/zangetsu")
                .load();
        flyway.migrate();
        return flyway;
    }
}

Конфигурация Flyway для базы данных Zabimaru

Аналогично, для базы данных Zabimaru мы настраиваем отдельный класс для миграций с Flyway. Здесь также указывается путь к миграциям для данной базы данных.

@Configuration
public class ZabimaruFlywayConfig {

    @Bean
    public Flyway zabimaruFlyway(@Qualifier("zabimaruDataSource") DataSource dataSource) {
        Flyway flyway = Flyway.configure()
                .dataSource(dataSource)
                .locations("classpath:db/migration/zabimaru")
                .load();
        flyway.migrate();
        return flyway;
    }
}

© Habrahabr.ru