HikariCP에서 Connection.isValid() 이 실행되는 시점
커넥션 풀에서 커넥션을 꺼낼 때
정확히는 두 가지 시점이 있습니다:
| 시점 | 조건 | 설명 |
| 커넥션 획득 시 | 항상 | getConnection() 호출 시 풀에서 꺼낸 커넥션이 유효한지 검증 |
| 유휴 커넥션 검사 | keepaliveTime 설정 시 | 풀에 놀고 있는 커넥션을 주기적으로 검증 (기본값: 0 = 비활성) |
즉, 애플리케이션에서 DB 요청할 때마다 SELECT 1 → 실제 쿼리 이렇게 매번 2개의 쿼리가 나가고 있었던 상황.COM_PING으로 전환하면 이 검증이 SQL 파서를 거치지 않는 프로토콜 명령으로 대체된다.
참고로 HikariCP 설정 중 관련된 것들:
| 설정 | 기본값 | 역할 |
| connectionTestQuery | 없음 (JDBC4 isValid 사용) | 커넥션 유효성 검증 쿼리 |
validationTimeout |
5000ms |
검증 타임아웃 |
keepaliveTime |
0 (비활성) |
유휴 커넥션 주기적 검증 간격 |
connectionTestQuery가 실행되는 시점
| 상황 | connectionTestQuery 설정됨 | connectionTestQuery 미설정 |
| getConnection() 호출 시 | SELECT 1 실행 | isValid() → COM_PING |
유휴 커넥션 주기 검사 (keepaliveTime > 0) |
SELECT 1 실행 |
isValid() → COM_PING |
유휴 커넥션 주기 검사 (keepaliveTime = 0) |
검사 안 함 | 검사 안 함 |
keepaliveTime이 0이든 아니든, getConnection() 할 때마다 커넥션 유효성 검증은 항상 실행됩니다.connectionTestQuery가 설정되어 있으면 매번 SELECT 1이 나가고, 미설정이면 COM_PING이 나가는 구조입니다.
HikariCP 헬스체크 전환: SELECT 1 → COM_PING
배경
기존에 HikariCP의 connectionTestQuery("SELECT 1")을 사용하여 커넥션 유효성을 검증하고 있었다.
이 방식은 커넥션 풀에서 커넥션을 꺼낼 때마다 SELECT 1 쿼리를 DB에 실행하므로, 불필요한 DB 조회 부하가 발생한다.
조사 결과, HikariCP는 JDBC4 드라이버 사용 시 connectionTestQuery를 설정하지 않으면Connection.isValid()를 호출하고, MySQL Connector/J는 이를 내부적으로 COM_PING 프로토콜 명령으로 처리한다.COM_PING은 SQL 쿼리가 아닌 MySQL 프로토콜 레벨의 경량 명령(1바이트)으로, SELECT 1 대비 오버헤드가 적다.
변경 내용
변경 사항
connectionTestQuery("SELECT 1") 설정을 제거(주석 처리)하여
HikariCP 기본 동작인 JDBC4 Connection.isValid() → COM_PING 방식으로 전환했다.
// Before
hikariConfig.setConnectionTestQuery("SELECT 1");
// After (제거)
// hikariConfig.setConnectionTestQuery("SELECT 1");
동작 원리
| 항목 | SELECT 1 방식 | COM_PING 방식 |
| 설정 | connectionTestQuery("SELECT 1") | 설정 없음 (기본값) |
| 동작 | SQL 쿼리 실행 | MySQL 프로토콜 명령 (0x0e) |
| 패킷 크기 | 수십 바이트 (SQL 문자열) | 5바이트 (헤더 4 + 명령 1) |
| DB 파서 | SQL 파서 경유 | 프로토콜 레벨 처리 |
| HikariCP 조건 | JDBC4 미지원 드라이버용 | JDBC4 드라이버 사용 시 기본값 |
환경 정보
- HikariCP: Spring Boot 내장
- MySQL Connector/J:
8.0.33(JDBC4 지원) - MySQL:
8.0.42(AWS RDS)
검증
1단계: profileSQL 로그를 통한 간접 검증
JDBC URL에 profileSQL=true를 추가하면 MySQL Connector/J가 실행하는 모든 SQL을 stderr로 출력한다.
connectionTestQuery("SELECT 1")설정 시 → 로그에SELECT 1출력됨connectionTestQuery미설정 시 → 로그에SELECT 1미출력 (SQL이 아닌COM_PING사용)
테스트 코드:connector/src/test/java/com/eximbay/datasource/HikariConnectionHealthCheckTest.java
2단계: tcpdump + Wireshark를 통한 직접 검증
네트워크 패킷 캡처로 COM_PING (0x0e) 패킷이 실제로 전송되는 것을 확인했다.
tcpdump 캡처
# 패킷 캡처 (en0 인터페이스, RDS 포트 지정)
sudo tcpdump -i en0 port 23306 -w /tmp/mysql_comping.pcap
# 별도 터미널에서 테스트 실행
./gradlew :connector:test --tests "com.eximbay.datasource.HikariConnectionHealthCheckTest.withoutTestQuery_logDoesNotContainSelect1"
# Ctrl+C로 캡처 중지
tcpdump 분석
# 클라이언트→서버 방향, length 5 패킷의 MySQL command byte 확인
tcpdump -r /tmp/mysql_comping.pcap -nn -X 'src {클라이언트IP} and dst port 23306' | grep -B2 -A4 "length 5"
MySQL 프로토콜 패킷 구조 (5바이트):
01 00 00 00 0e
│ │ └─ command byte: 0x0e = COM_PING (14)
│ └──── sequence id: 0
└───────────── payload length: 1
캡처 결과:
⚪ COM_QUIT (0x01): 커넥션 종료
🔵 COM_PING (0x0e): isValid() → COM_PING 호출 확인
🔵 COM_PING (0x0e): isValid() → COM_PING 호출 확인
⚪ COM_QUIT (0x01): 커넥션 종료
Wireshark 확인
.pcap파일을 Wireshark에서 열기- 비표준 포트이므로
Analyze → Decode As...에서 TCP port23306을MySQL로 설정 - Display Filter에
mysql.command == 14입력 COM_PING패킷만 필터링되어 표시됨
mysql.command == 14는 Wireshark가 패킷 바이너리를 MySQL 프로토콜 규격으로 파싱한 내부 필드를 기준으로 필터링한다.
화면에 보이는 텍스트 검색이 아니라, 디코딩된 프로토콜 필드 값으로 매칭한다.
참고 필터:
| 필터 | 설명 |
| mysql.command == 14 | COM_PING |
mysql.command == 1 |
COM_QUIT |
mysql.command == 3 |
COM_QUERY (SELECT 등) |
결론
connectionTestQuery("SELECT 1") 제거만으로 HikariCP가 자동으로 COM_PING 기반 헬스체크로 전환되며,profileSQL 로그와 네트워크 패킷 캡처 양쪽에서 이를 검증 완료했다.
SELECT 1 검증 쿼리 확인 방법
운영 중인 MySQL에서 SELECT 1 헬스체크가 실제로 얼마나 실행되고 있는지 쿼리로 확인할 수 있다.
Com_select 카운터 확인
-- SELECT 문 실행 횟수 (서버 시작 이후 누적)
SHOW GLOBAL STATUS LIKE 'Com_select';
connectionTestQuery("SELECT 1") 제거 전후로 이 값의 증가 속도를 비교하면 효과를 확인할 수 있다.
일정 시간 동안 SELECT 1 발생량 측정
-- 1) 현재 값 기록
SELECT VARIABLE_VALUE INTO @before
FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Com_select';
-- 2) 일정 시간 대기 (예: 10초)
DO SLEEP(10);
-- 3) 차이 계산
SELECT VARIABLE_VALUE - @before AS select_count_in_10s
FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Com_select';
General Log로 SELECT 1 직접 확인
-- General Log 활성화 (운영 환경에서는 짧은 시간만 사용)
SET GLOBAL general_log = 'ON';
SET GLOBAL log_output = 'TABLE';
-- 일정 시간 후 SELECT 1 쿼리 확인
SELECT event_time, command_type, argument
FROM mysql.general_log
WHERE argument LIKE 'SELECT 1%'
ORDER BY event_time DESC
LIMIT 20;
-- General Log 비활성화
SET GLOBAL general_log = 'OFF';
General Log는 모든 쿼리를 기록하므로 성능에 영향을 준다.
운영 환경에서는 짧은 시간만 활성화하고 반드시 끌 것.
Performance Schema로 확인
-- SELECT 1 쿼리의 실행 통계 확인
SELECT DIGEST_TEXT, COUNT_STAR, AVG_TIMER_WAIT/1000000000 AS avg_ms
FROM performance_schema.events_statements_summary_by_digest
WHERE DIGEST_TEXT LIKE 'SELECT ?%'
ORDER BY COUNT_STAR DESC
LIMIT 10;
SELECT ?는 MySQL이 SELECT 1을 정규화한 형태다.COUNT_STAR가 커넥션 풀 크기 × 요청 수에 비례하면 헬스체크 쿼리일 가능성이 높다.
참고
- HikariCP 공식 문서 — JDBC4 드라이버 사용 시
connectionTestQuery설정하지 않을 것을 권장
https://github.com/brettwooldridge/HikariCP#frequently-used
'IT 지식' 카테고리의 다른 글
| 한글이 왜 깨질까? 캐릭터셋과 인코딩에 대해서 알아보자 (1) | 2025.09.28 |
|---|---|
| macOS에서 계정명(short name) 변경하기 - Sequoia 15.1 경험 정리 (0) | 2025.09.21 |
| [맥북] 한영키 변환 속도 개선 (0) | 2025.06.17 |
| [PC/문제해결] Windows 10 AMD Radeon HD 4000 모니터 화면 버그 (0) | 2022.04.02 |
| [IT 지식] 소프트웨어 아키텍트 역할군 ( AA, TA, DA, BA ??? ) (0) | 2022.01.20 |