HikariCP Tuning - MySQL 편
2023년 03월 06일
Database
📕 목차
들어가며
이번 글은 데이터베이스 연결을 효과적으로 관리하기 위한 커넥션 풀의 일종인 HikariCP에서 데이터베이스 성능을 향상시키기 위해 사용할 수 있는 몇 가지 옵션에 대해 알아보겠습니다.
HikariCP란?
공식문서에서 다음과 같이 설명하고 있습니다. 빠르고, 간단하며, 신뢰할 수 있다. HikariCP는 zero-overhead JDBC 커넥션 풀
이다. 약 130Kb의 라이브러리로 매우 가볍다.
Fast, simple, reliable. HikariCP is a “zero-overhead” production ready JDBC connection pool. At roughly 130Kb, the library is very light.
튜닝 옵션
여기서 소개할 튜닝 옵션은 MySQL을 기준으로 작성되었습니다.
useLocalSessionState
쓰기 및 업데이트 작업을 할때, 세션의 상태가 읽기 모드인지 쓰기 모드인지와 검사하여 쓰기 및 업데이트 작업을 할지 결정합니다. 이와 같은 상태를 저장하는 방법에는 두 가지가 있습니다.
- 데이터베이스에 저장
- 애플리케이션의 로컬 상태값을 통해서 쓰기, 읽기 작업을 수행
MySQL8 을 기준으로 기본설정은 false 입니다. 기본 설정은 데이터베이스에 세션의 상태를 저장하고, 쓰기 및 업데이트 시 데이터베이스로 쿼리를 날려서 세션의 상태를 확인합니다. 예시를 통해서 쿼리가 어떤식으로 실행되는지 알아보도록 하겠습니다.
예) useLocalSessionState = false인 경우
-- ; 데이터를 쓰기 전에 트랜잭션의 읽기 모드 상태를 확인합니다.
SELECT @@session.transaction_read_only
INSERT INTO xxxx (a, b, c) VALUES (1, 2, 3)
-- ; 데이터를 업데이트 하기 전에 트랜잭션의 읽기 모드 상태를 확인합니다.
SELECT @@session.transaction_read_only
UPDATE xxx SET yyy = 1 WHERE id = 1234
위처럼 데이터베이스에 세션의 상태를 저장하고, 쓰기 및 업데이트 시 세션의 상태를 확인하는 방식은 데이터베이스와 통신을 한다는 점에서 오버헤드가 발생합니다. 이 옵션을 활성화하면, 로컬 세션 상태를 사용해 데이터베이스와 통신 없이 세션의 상태를 확인할 수 있기 때문에 오버헤드를 줄일 수 있습니다.
Q. 성능에 이점이 있다면 useLocalSessionState
의 기본값은 왜 false일까?
이 옵션의 기본값이 false인 이유는 주의해서 사용해야하기 때문입니다. 로컬세션상태를 사용하여 세션의 상태를 관리하게되면, 애플리케이션을 사용할 때 조심해야 합니다. 왜나하면, 로컬 세션 상태를 사용하면서 stmt.execute("set_auto_commit=0")
처럼 상태를 변경하는 쿼리를 실행하면, 세션의 상태가 불일치할 수 있기 때문입니다.
그래서 이 옵션을 사용하려면 애플리케이션 내에서 트랜잭션 구간에서 세션의 상태를 변경하는 쿼리를 실행하지 않도록 주의해야합니다.
결론
로컬에서 세션의 상태를 관리하는 방식을 통해 데이터베이스와 통신 없이 세션의 상태를 확인할 수 있습니다. 하지만, 애플리케이션에서 세션의 상태를 변경하는 쿼리를 실행하지 않도록 주의해야합니다.
useServerPrepStmts
이 옵션은 데이터 베이스 쿼리를 실행하기위해 사용하는 prepared statement를 데이터베이스 서버에 캐싱함으로써 성능을 향상시키기 위해 사용될 수 옵션입니다.
클라이언트와 서버 사이드에서 사용되는 방식의 비교를 통해서 알아보도록 하겠습니다.
Client Side Prepared Statements
prepareStatement = conn.prepareStatement("SELECT * FROM table WHERE id = ?");
prepareStatement.setInt(1, 1); // ?에 1을 바인딩합니다.
prepareStatement.executeQuery();
위의 코드는 SELECT * FROM table WHERE id = 1
과 같이 완성된 쿼리를 서버로 전송합니다.
클라이언트 사이드 prepared statement는 클라이언트에서 쿼리를 완성해서 전달하는 방식으로 동작합니다. 이를 통해 서버에서는 매번 구문분석과 실행계획 과정을 거치게 됩니다.
프로토콜 레벨에서는, COM_QUERY 라는 텍스트 기반 프로토콜을 사용해서 전송합니다.
Server Side Prepared Statements
서버 사이드 prepared statement는 클라이언트에서 쿼리를 완성해서 전달하는 방식이 아닌, prepared statement를 생성하고, prepared statement를 실행하는 방식으로 동작합니다. 각각의 단계는 다음과 같습니다.
- 클라이언트에서 COM_STMT_PREPARE 프로토콜을 사용해서 데이터베이스 서버에 쿼리 스트링을 전달하기 위한, prepared statement를 생성합니다.
- 클라이언트에서 COM_STMT_EXECUTE 프로토콜을 사용해서 데이터베이스 서버에 prepared statement를 실행합니다.
서버 사이드 방식으로 prepared statement를 사용하면, 구문분석과 실행계획 (최적화)를 한 번만 수행하면 되기 때문에 (캐싱), 클라이언트 사이드 prepared statement보다 더 빠르게 동작합니다.
이 옵션도 만능일까?
서버 사이드에서 사용할 수 없는 몇 가지 구문이 있기 때문에, 사용할 수 없는 몇 가지 경우가 있을 수 있습니다. 이 경우에는, 서버 사이드 prepared statement를 사용하도록 요청하고, 실패한다면 클라이언트 사이드 prepared statment를 사용하기때문에 통신을 위한 불필요한 오버헤드가 발생할 수 있습니다.
참고자료
- https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-configuration-properties.html
- https://forums.mysql.com/read.php?39,626495,626511#msg-626511
- https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration
- https://stackoverflow.com/questions/32286518/whats-the-difference-between-cacheprepstmts-and-useserverprepstmts-in-mysql-jdb