-
HikariCP 설정 일부분 들여다 보기Database 2023. 2. 26. 15:41
이번 글에서는 HikariCP를 설정하면서, 각 설정이 어떤 영향을 미치는지 확인해 보도록 하겠습니다.
https://github.com/brettwooldridge/HikariCP HikariCP 공식 문서에 가보면, 설정들에 대해 자세한 코멘트가 되어 있습니다.여기서 확인해 볼 HikariCP 설정 목록
- connectionTimeout
- validationTimeout
- connectionTestQuery
참고로, 아래 4개 항목은 내용 정리만 되었습니다.
- minimumIdle
- maximumPoolSize
- idleTimeout
- maxLifetime
전체 코드
우선 전체 코드를 한번 보면 좋을 것 같네요.
보시는데 무리만 없을 정도로 정리 했습니다.
그리고, 중간중간 이해되지 않는 코드가 몇 줄 있을 수 있는데 간단한 테스트를 하는데는 문제가 없을 거에요.package junseok.snr.study.hikaricp; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; import java.util.Scanner; import java.util.concurrent.CompletableFuture; public class HikariCPTest { enum Status { RUNNING, QUERY, CONNECTION, END } private static final Logger log = LoggerFactory.getLogger(HikariCPTest.class); private static final HikariDataSource dataSource = createDataSource(getHikariConfig()); private static Status status = Status.RUNNING; public static void main(String[] args) throws InterruptedException { final Scanner scanner = new Scanner(System.in); Thread.sleep(1000); while (status == Status.RUNNING) { process(scanner); } dataSource.close(); } private static void process(Scanner scanner) { log.info(">>>>> Process Running"); final String command = scanner.next(); connectionIfCommandConnection(command); queryIfCommandQuery(command); endIfCommandEnd(command); } private static void endIfCommandEnd(String command) { if (command.equals(Status.END.name())) { status = Status.END; log.info(">>>>> Process End"); } } private static void connectionIfCommandConnection(String command) { if (command.equals(Status.CONNECTION.name())) { try (Connection connection = dataSource.getConnection()) { log.info(">>>>> getConnection ::: {}", connection); } catch (Exception exception) { log.error(">>>>> CONNECTION Exception", exception); } } } private static void queryIfCommandQuery(String command) { if (command.equals(Status.QUERY.name())) { CompletableFuture.supplyAsync(() -> { try (final Connection con = dataSource.getConnection(); final Statement statement = con.createStatement()) { Thread.sleep(2000); con.isValid(1); statement.setQueryTimeout(10); processQuery(statement); } catch (Exception exception) { log.error(">>>>> QUERY Statement Exception", exception); } return 0; }); } } private static void processQuery(Statement statement) { final String sql = "SELECT * FROM users a, users b\n" + "WHERE a.ip_address LIKE '%1%'\n" + " AND b.last_name LIKE '%a%'\n" + " AND a.first_name LIKE '%a%';"; try (final ResultSet resultSet = statement.executeQuery(sql)) { log.info(">>>>> QUERY Print ::: {}", resultSet.next()); } catch (Exception exception) { log.error(">>>>> QUERY ResultSet Exception", exception); } } private static HikariDataSource createDataSource(HikariConfig config) { return new HikariDataSource(config); } private static HikariConfig getHikariConfig() { final HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mariadb://localhost:13300/test_db"); config.setUsername("junseok"); config.setPassword("1234"); config.addDataSourceProperty("cachePrepStmts", "true"); config.addDataSourceProperty("prepStmtCacheSize", 250); config.addDataSourceProperty("preStmtCacheSqlLimit", 2048); config.setMinimumIdle(5); config.setMaximumPoolSize(10); config.setConnectionTimeout(3000000); config.setIdleTimeout(10000); config.setValidationTimeout(30001); config.setMaxLifetime(60000); final String connectionTestQuery = "SELECT * FROM users a, users b\n" + "WHERE a.ip_address LIKE '%1%'\n" + " AND b.last_name LIKE '%a%'\n" + " AND a.first_name LIKE '%a%' LIMIT 2500000;"; config.setConnectionTestQuery(connectionTestQuery); return config; } }
HikariCP 설정
validationTimeout
사실 validationTimeout 은 체감할 수 있을 정도로 테스트 하지 못했습니다. 문서와 블로깅을 통해서 확인했을 때는 Connection을 얻기 위해서 Connection.isValid() 메서드를 호출하는 데 이때, validationTimeout에 설정된 시간보다 커넥션을 가져오면서 검증하는데 걸리는 시간이 더 길 경우 Exception이 발생된다고 되어 있는데요.
validationTimeout 시간을 5초로 설정한 후, connectionTestQuery를 실행했을 때, connectionTestQuery 의 조회시간 5초를 넘겨도 Exception은 발생하지 않았습니다.connectionTestQuery
공식문서를 보면 다음과 같이 말하고 있습니다.
드라이버가 JDBC4를 지원하는 경우 이 속성을 설정하지 않는 것이 좋습니다. 이것은 JDBC4 Connection.isValid() API를 지원하지 않는 "레거시" 드라이버용입니다.
그러면 아래와 같이 설정했을 때 어떤 일들이 벌어지는지 한번 살펴 볼게요.final String connectionTestQuery = "SELECT * FROM users a, users b\n" + "WHERE a.ip_address LIKE '%1%'\n" + " AND b.last_name LIKE '%a%'\n" + " AND a.first_name LIKE '%a%' LIMIT 2500000;"; config.setConnectionTestQuery(connectionTestQuery);
connectionTimeout
요점만 정리하면, connectionTimeout은 쿼리 타임아웃이 아닙니다.
우리가 쿼리 타임 아웃을 설정하려면, statement timeout을 설정해야 합니다.
이건, mybatis, jpa를 쓸 때 모두 공통으로 설정을 적용할 수도 있습니다.
이 커넥션타임아웃은, DB에 쿼리를 실행해 보내기 위해, connection을 얻어야 하는데,
이때 connection을 요청해서 가져오는데 걸리는 시간입니다.
1분이 넘게 실행되는 쿼리가 있어서 서버 부하가 있는 경우, 1분 이상 쿼리가 실행되지 않도록 하기 위해
여기에 설정해 봤자, 쿼리는 1분이 넘도록 실행됩니다.
이 설정은 connection을 가져오는데 사용되는 설정이기 때문이지요.
쿼리 실행시간이 1분이 넘는경우 강제로 실행중인 쿼리를 종료하기 위해서는 statement timeout 설정을 해줘야 합니다.
connectionTestQuery가 사용되는 순간
서버가 올라가면서 HikariCP ConnectionPool을 만들 때 어느 부분에서 호출하는지 알아볼게요.
먼저 HikariConfig 를 파라미터로 받는 HikariDataSource 생성자를 사용해서 HikariDataSource를 인스턴스화 합니다.
여기서 보면, new HikariPool(this); 절이 보입니다. 여기서 connectionTestQuery를 실행합니다.
한번 더 들어가 보겠습니다.
여기서 보면 가운데 부분에 checkFailFast(); 가 보이실 거에요. 여기서 connectionTestQuery를 실행합니다.
한번 더 들어가 보겠습니다.
여기서 보시면 createPoolEntry() 메서드가 보이실 거에요. 여기서 connectionTestQuery를 실행합니다.
한번 더 들어가 보겠습니다.
여기에 보시면 try 절 다음 줄에 newPoolEnrty() 가 보이실 거에요. 여기서 connectionTestQuery를 실행합니다.
한번 더 들어가 보면 아래 코드가 나옵니다.
여기서 newConnection() 에서 connectionTestQuery를 실행합니다.여기서 한번 더 들어가 보겠습니다.
여기에 보시면 setUpConnection(connection); 이 보이실 거에요. 여기서 connectionTestQuery를 실행합니다.
여기서 checkDriverSupport(connection); 에서 connectionTestQuery를 실행합니다.
여기에서 마지막으로 checkValidationSupport를 호출하면서 connectionTestQuery가 실행됩니다.
여기에서 executeSql이 실행됩니다.
보시면 debuging 포인트를 걸어둔 부분에 SELECT 1이 보이실 겁니다.
minimumIdle
풀에 유지해야 하는 최소한의 유휴 커넥션 수입니다. 이 값 이상의 유휴 커넥션을 유지하고 있으며, 유휴 커넥션이 필요할 경우 빠르게 제공할 수 있습니다.
maximumPoolSize
풀에서 생성 가능한 최대 커넥션 수입니다. 이 값은 너무 작으면 커넥션을 가져올 수 없어 서비스 지연이 발생할 수 있으며, 너무 크면 서버 리소스를 낭비하게 됩니다.
idleTimeout
커넥션이 유휴 상태로 있을 때, 커넥션 풀에서 커넥션을 제거하기 전에 대기할 시간입니다. 이 값은 너무 짧으면 유휴 커넥션을 제거하여 서비스 지연을 초래하고, 너무 길면 서버 리소스를 낭비합니다.
maxLifetime
커넥션이 유효한 최대 시간입니다. 여기서 설정한 시간이 넘어가면 커넥션을 제거하고, 새로운 커넥션을 생성하여 사용하도록 합니다. 이 값이 너무 짧으면 유효한 커넥션을 제거해서 서비스 지연을 초래합니다. 그리고, 너무 길면 사용되지 않는데 메모리를 계속 점유하고 있기 때문에 서버의 메모리 리소스를 낭비합니다.
더 많은 테스트와, 더 많은 것을 알게 됐지만, 역시, 모든 수행 내용을 글로 정리한다는 것은 너무나도 오랜 시간이 걸리는 일이네요.
나중에 계속 추가로 정리해야지 했지만, 쉽지 않네요.다음부터는 한번 작성한 글은, 끝을 봐야겠다는 생각이들어요.
'Database' 카테고리의 다른 글
MariaDB Lock(실전) (0) 2023.04.01 MariaDB Index 1편 (0) 2023.03.20 MariaDB Explain (0) 2022.08.22 [MariaDB]CREATE DATABASE/USER, GRANT, FLUSH (0) 2021.04.27 [Oracle] User Password 만료 기간을 무기한으로 변경하는 방법 (0) 2021.04.20