ABOUT ME

와주셔서 감사합니다. 좋은 글을 많이 쓰겠습니다.

Today
Yesterday
Total
  • [HikariCP] keepaliveTime 와 관련된 여러가지 실험
    Java 2024. 7. 22. 18:22
    반응형

    - 목차

     

    keepaliveTime 이란 ?

    keepaliveTime 설정은 Idle Connection 과 관련이 있습니다.

    HikariCP Connection Pool 에서 Idle Connection 은 idleTimeout 이후에 제거됩니다.

    Idle Connection 이란 Connection 과 데이터베이스 사이에 맺어진 TCP Socket 사이에 어떠한 Packet 도 전달되지 않는 상태의 Socket 을 의미합니다.

    즉, 어떠한 쿼리 요청도 발생하지 않는 Connection 을 Idle Connection 이라하며, 이는 10분이라는 기본값을 가집니다.

    10분 동안 통신이 전무한 Connection 은 제거됩니다.

     

    keepaliveTime 은 Idle Connection 으로 연결된 TCP Socket 으로 Ping 요청을 보내는 주기입니다.

    이는 기본값으로 Disabled 되어 있습니다. (Default: 0 (disabled))

    따라서 keepaliveTime 은 idleTimeout 보다 작게 설정하게 되면, Idle Connection 는 꾸준히 Connection 을 유지하게 됩니다.

     

    KeepaliveTime 과 test query 살펴보기.

     

    아래와 같이 HikariCP 의 ConnectionPool 를 설정합니다.

    KeepaliveTime 을 1분으로 지정하고, 그 외 IdleTimeout 과 MaxLifetime 을 Keepalive Time 보다 큰 값을 설정합니다.

    HikariConfig config = new HikariConfig();
    config.setJdbcUrl("jdbc:mysql://mysql:3306/test?allowPublicKeyRetrieval=true&useSSL=false");
    config.setUsername("root");
    config.setPassword("1234");
    config.setMinimumIdle(2);
    config.setMaximumPoolSize(5);
    config.setKeepaliveTime(60000);
    config.setIdleTimeout(70000);
    config.setConnectionTestQuery("select count(*) from test.member;");
    config.setMaxLifetime(1200000);

     

    그리고 Java Application 을 실행하게 되면 아래와 같이 "select count(*) from test.member" 라는 요청이 1분 간격으로 MySQL 서버로 전송됨을 확인할 수 있습니다.

    저는 ConnectionTestQuery 로 "select count(*) from test.member" 를 지정하였기 때문에 이러한 쿼리가 1분 간격으로 요청됩니다.

    01:21:18.619680 IP fbbbd46ffc0b.57832 > mysql.mysql.mysql: Flags [P.], seq 1327:1367, ack 1399, win 524, options [nop,nop,TS val 2718954740 ecr 159002098], length 40
    	0x0000:  4500 005c 8348 4000 4006 5f2a ac12 0003  E..\.H@.@._*....
    	0x0010:  ac12 0002 e1e8 0cea 33f6 b53c 64da a2b8  ........3..<d...
    	0x0020:  8018 020c 5878 0000 0101 080a a20f f4f4  ....Xx..........
    	0x0030:  097a 2df2 2400 0000 0300 0173 656c 6563  .z-.$......selec
    	0x0040:  7420 636f 756e 7428 2a29 2066 726f 6d20  t.count(*).from.
    	0x0050:  7465 7374 2e6d 656d 6265 723b            test.member;
        
    01:22:18.561244 IP fbbbd46ffc0b.57832 > mysql.mysql.mysql: Flags [P.], seq 1367:1407, ack 1456, win 524, options [nop,nop,TS val 2719014681 ecr 159062042], length 40
    	0x0000:  4500 005c 834a 4000 4006 5f28 ac12 0003  E..\.J@.@._(....
    	0x0010:  ac12 0002 e1e8 0cea 33f6 b564 64da a2f1  ........3..dd...
    	0x0020:  8018 020c 5878 0000 0101 080a a210 df19  ....Xx..........
    	0x0030:  097b 181a 2400 0000 0300 0173 656c 6563  .{..$......selec
    	0x0040:  7420 636f 756e 7428 2a29 2066 726f 6d20  t.count(*).from.
    	0x0050:  7465 7374 2e6d 656d 6265 723b            test.member;
        
    01:23:18.504583 IP fbbbd46ffc0b.57832 > mysql.mysql.mysql: Flags [P.], seq 1407:1447, ack 1513, win 524, options [nop,nop,TS val 2719074624 ecr 159121980], length 40
    	0x0000:  4500 005c 834c 4000 4006 5f26 ac12 0003  E..\.L@.@._&....
    	0x0010:  ac12 0002 e1e8 0cea 33f6 b58c 64da a32a  ........3...d..*
    	0x0020:  8018 020c 5878 0000 0101 080a a211 c940  ....Xx.........@
    	0x0030:  097c 023c 2400 0000 0300 0173 656c 6563  .|.<$......selec
    	0x0040:  7420 636f 756e 7428 2a29 2066 726f 6d20  t.count(*).from.
    	0x0050:  7465 7374 2e6d 656d 6265 723b            test.member;

     

    connectionTestQuery 가 지정되지 않은 상황.

    만약 connectionTestQuery 가 지정되지 않은 상황에서는 아래와 같이 "0x0e" 에 해당하는 Ping 요청이 전달되게 됩니다.

    02:28:35.011665 IP fbbbd46ffc0b.34658 > mysql.mysql.mysql: Flags [P.], seq 1277:1282, ack 1351, win 524, options [nop,nop,TS val 2722991198 ecr 163062227], length 5
    	0x0000:  4500 0039 602a 4000 4006 826b ac12 0003  E..9`*@.@..k....
    	0x0010:  ac12 0002 8762 0cea 1c0d eed9 061e 1b5c  .....b.........\
    	0x0020:  8018 020c 5855 0000 0101 080a a24d 8c5e  ....XU.......M.^
    	0x0030:  09b8 21d3 0100 0000 0e                   ..!......

     

    https://dev.mysql.com/doc/dev/mysql-server/9.1.0/page_protocol_com_ping.html

     

    MySQL: COM_PING

    Check if the server is alive Payload TypeNameDescription int<1> command 0x0E: COM_PING ReturnsOK_Packet See alsomysql_ping, dispatch_command

    dev.mysql.com

     

    KeepaliveTime 과 MySQL 의 wait_timeout 관계.

    MySQL 은 Idle Connection 을 wait_timeout 으로 지정된 시간까지만 유지합니다.

    만약 Idle Connection 이 wait_timeout 시간을 경과하게 되면 Idle Connection 은 강제로 종료됩니다.

    MySQL 의 wait_timeout 기본값은 8시간이며, 8시간이 경과한 Idle Connection 은 종료됩니다.

     

    일반적으로 show processlist; 명령어를 통해서 아래와 같은 결과를 확인할 수 있습니다.

    show processlist;
    +----+-----------------+------------------+------+---------+------+------------------------+------------------+
    | Id | User            | Host             | db   | Command | Time | State                  | Info             |
    +----+-----------------+------------------+------+---------+------+------------------------+------------------+
    |  5 | event_scheduler | localhost        | NULL | Daemon  | 5366 | Waiting on empty queue | NULL             |
    | 27 | root            | 172.18.0.3:43234 | test | Sleep   |   27 |                        | NULL             |
    | 28 | root            | 172.18.0.3:43250 | test | Sleep   |   27 |                        | NULL             |
    | 29 | root            | localhost        | NULL | Query   |    0 | init                   | show processlist |
    +----+-----------------+------------------+------+---------+------+------------------------+------------------+
    4 rows in set (0.00 sec)

     

    그리고 Time 이라는 칼럼의 의미하는 값이 Connection 의 지속 시간입니다.

    위 결과를 보면 Sleep Command 를 가지는 2개의 Connection 이 Idle Connection 에 해당합니다.

    그리고 27초간 Idle Time 이 지속되고 있습니다.

    만약 Query 가 실행되어서 Active Connection 으로 변경된다면 이 시간은 0초로 초기화됩니다.

     

    show processlist;
    +----+-----------------+------------------+------+---------+------+------------------------+------------------+
    | Id | User            | Host             | db   | Command | Time | State                  | Info             |
    +----+-----------------+------------------+------+---------+------+------------------------+------------------+
    |  5 | event_scheduler | localhost        | NULL | Daemon  | 5485 | Waiting on empty queue | NULL             |
    | 27 | root            | 172.18.0.3:43234 | test | Sleep   |    1 |                        | NULL             |
    | 28 | root            | 172.18.0.3:43250 | test | Sleep   |    2 |                        | NULL             |
    | 29 | root            | localhost        | NULL | Query   |    0 | init                   | show processlist |
    +----+-----------------+------------------+------+---------+------+------------------------+------------------+
    4 rows in set (0.00 sec)

     

     

    Keepalive Time 은 이러한 wait_timeout 으로 인한 Idle Connection 의 강제적인 종료를 방지하고자 지속적인 Query 요청 또는 Ping 요청을 보내는 것을 목적으로 합니다.

    Idle Connection 은 Query / Ping 요청을 보내면 Active Connection 으로 상태가 변경되고, Time 이 0 초로 Reset 됩니다.

     

    KeepaliveTime 의 최소값.

    HikariCP 의 keepalive Time 의 최소값은 30초로 제한됩니다.

     

     

    https://github.com/brettwooldridge/HikariCP?utm_source=chatgpt.com

     

    GitHub - brettwooldridge/HikariCP: 光 HikariCP・A solid, high-performance, JDBC connection pool at last.

    光 HikariCP・A solid, high-performance, JDBC connection pool at last. - brettwooldridge/HikariCP

    github.com

     

     

    관련 명령어들.

    
    cat <<EOF> /tmp/HikariPoolMonitor.java
    import com.zaxxer.hikari.HikariConfig;
    import com.zaxxer.hikari.HikariDataSource;
    import java.sql.Connection;
    import java.sql.Statement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    public class HikariPoolMonitor {
        public static void main(String[] args) throws InterruptedException {
            // HikariCP 설정
            HikariConfig config = new HikariConfig();
            config.setJdbcUrl("jdbc:mysql://mysql:3306/test?allowPublicKeyRetrieval=true&useSSL=false");
            config.setUsername("root");
            config.setPassword("1234");
            config.setMinimumIdle(2);
            config.setMaximumPoolSize(5);
            config.setKeepaliveTime(40000);
            config.setIdleTimeout(70000);
            config.setMaxLifetime(1200000);
    
            // Hikari DataSource 생성
            HikariDataSource dataSource = new HikariDataSource(config);
            Connection c1 = null;
            Connection c2 = null;
            Connection c3 = null;
            Connection c4 = null;
            Connection c5 = null;
            try {c1 = dataSource.getConnection();} catch (SQLException e) {e.printStackTrace();}
            try {c2 = dataSource.getConnection();} catch (SQLException e) {e.printStackTrace();}
            try {c3 = dataSource.getConnection();} catch (SQLException e) {e.printStackTrace();}
            try {c4 = dataSource.getConnection();} catch (SQLException e) {e.printStackTrace();}
            try {c5 = dataSource.getConnection();} catch (SQLException e) {e.printStackTrace();}
    
    
            try {Thread.sleep(1000L); c1.close();} catch (Exception e) {e.printStackTrace();}
            try {c2.close();} catch (SQLException e) {e.printStackTrace();}
            try {c3.close();} catch (SQLException e) {e.printStackTrace();}
            try {c4.close();} catch (SQLException e) {e.printStackTrace();}
            try {c5.close();} catch (SQLException e) {e.printStackTrace();}
            // 5초 단위로 HikariCP 풀 상태를 출력하는 루프
            while (true) {
                printHikariPoolStatus(dataSource);
                Thread.sleep(5000); // 5초 대기
            }
    
            // 데이터소스 종료
            // dataSource.close();
        }
    
        // HikariCP 풀 상태 출력 함수
        private static void printHikariPoolStatus(HikariDataSource ds) {
            System.out.println("========== HikariCP Pool 상태 ==========");
            System.out.println("총 커넥션 수: " + ds.getHikariPoolMXBean().getTotalConnections());
            System.out.println("활성 커넥션 수: " + ds.getHikariPoolMXBean().getActiveConnections());
            System.out.println("유휴 커넥션 수: " + ds.getHikariPoolMXBean().getIdleConnections());
            System.out.println("대기 중인 스레드 수: " + ds.getHikariPoolMXBean().getThreadsAwaitingConnection());
            System.out.println("====================================\n");
        }
    }
    EOF
    
    docker network create mysql 
    cat <<EOF> /tmp/my.cnf
    [mysqld]
    skip_ssl
    tls_version=DISABLED
    EOF
    docker run -d --platform linux/amd64 --network mysql --name mysql --hostname mysql -v /tmp/my.cnf:/etc/mysql/conf.d/custom.cnf -p 3306:3306 -e MYSQL_ROOT_PASSWORD=1234 mysql:8.0.23
    docker run -it --network mysql --rm -v /tmp/HikariPoolMonitor.java:/tmp/HikariPoolMonitor.java openjdk:11 /bin/bash
    
    cd /tmp/
    wget https://repo1.maven.org/maven2/com/zaxxer/HikariCP/5.1.0/HikariCP-5.1.0.jar
    wget https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar
    wget https://repo1.maven.org/maven2/org/slf4j/slf4j-simple/1.7.36/slf4j-simple-1.7.36.jar
    wget https://repo1.maven.org/maven2/com/mysql/mysql-connector-j/8.0.33/mysql-connector-j-8.0.33.jar -P /tmp
    
    javac -cp HikariCP-5.1.0.jar:slf4j-api-1.7.36.jar:slf4j-simple-1.7.36.jar:mysql-connector-j-8.0.33.jar HikariPoolMonitor.java
    java -cp .:HikariCP-5.1.0.jar:slf4j-api-1.7.36.jar:slf4j-simple-1.7.36.jar:mysql-connector-j-8.0.33.jar HikariPoolMonitor
    
    create database test;
    use test;
    create table test.member (id int primary key not null auto_increment);
    insert into member values (),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),();

     

     

    반응형
Designed by Tistory.