밤사이 트래픽이 없다가 아침 첫 요청에서 Communications link failure나 connection reset이 터진 적이 있나요? 코드는 멀쩡한데 간헐적으로만 나는 끊김이라면, 십중팔구 MySQL이 유휴 커넥션을 먼저 끊어버린 것입니다. 앱은 풀에 들고 있던 죽은 커넥션을 모르고 재사용하다 실패합니다.
누가 커넥션을 끊는가
MySQL은 클라이언트가 아무 쿼리도 안 보내고 가만히 있는 시간을 wait_timeout(기본 28800초, 8시간)으로 제한합니다. 이 시간을 넘기면 서버가 일방적으로 연결을 닫습니다.
| 변수 | 의미 | 기본값 |
|---|---|---|
wait_timeout | 일반 커넥션 유휴 허용 시간 | 28800초 |
interactive_timeout | 대화형 클라이언트 유휴 허용 시간 | 28800초 |
max_connections | 동시 커넥션 상한 | 151 |
현재 값은 이렇게 확인합니다.
SQL
SHOW VARIABLES LIKE 'wait_timeout';
SHOW VARIABLES LIKE 'max_connections';
SHOW STATUS LIKE 'Threads_connected';
문제는 커넥션 풀의 유휴 타임아웃이 wait_timeout보다 길 때 생깁니다. 풀은 "아직 살아있다"고 믿는데 서버는 이미 끊은 상태라, 그 커넥션을 꺼내 쓰는 순간 에러가 납니다.
끊김 진단 순서
- 에러 시점 패턴 확인 — 트래픽이 적은 새벽·점심 직후에 몰린다면 유휴 끊김이 강하게 의심됩니다.
- 서버 타임아웃 값 확인 — 위
SHOW VARIABLES로wait_timeout을 봅니다. 클라우드 RDS는 파라미터 그룹에서 더 짧게 잡혀 있는 경우가 많습니다. - 풀 유휴 시간과 비교 — 앱 커넥션 풀의 최대 유휴 시간이
wait_timeout보다 짧은지 확인합니다. 길면 그게 원인입니다. - 끊긴 커넥션 카운트 —
SHOW STATUS LIKE 'Aborted_clients';값이 시간이 지나며 늘어나면 서버가 유휴 연결을 끊고 있다는 신호입니다.
해결: 풀이 먼저 정리하게 한다
서버 wait_timeout을 무작정 늘리기보다, 클라이언트 풀이 서버보다 먼저 죽은 커넥션을 걷어내게 하는 게 정석입니다. HikariCP 예시입니다.
PROPERTIES
# wait_timeout(예: 600초)보다 짧게 잡아 서버가 끊기 전에 풀이 회수
spring.datasource.hikari.max-lifetime=570000
spring.datasource.hikari.idle-timeout=300000
spring.datasource.hikari.keepalive-time=120000
핵심은 max-lifetime을 서버 wait_timeout보다 수십 초 짧게 두는 것입니다. 그래야 풀이 항상 먼저 커넥션을 폐기·재생성합니다. 필요하면 서버 값도 워크로드에 맞게 조정합니다.
SQL
SET GLOBAL wait_timeout = 600;
단, SET GLOBAL은 재시작하면 사라지므로 영구 적용은 설정 파일(my.cnf)이나 RDS 파라미터 그룹에 넣습니다.
요점 정리
- 간헐적
connection reset은 대부분 서버가 유휴 커넥션을wait_timeout으로 끊은 결과다. - 진단은 에러 시점 패턴
→서버 타임아웃 값→풀 유휴 시간 비교→Aborted_clients순. - 해결의 핵심은 풀의
max-lifetime을 서버wait_timeout보다 짧게 두는 것.
커넥션 풀 동작과 타임아웃 설정을 직접 바꿔가며 끊김을 재현·해결하는 실습은 데이터베이스 트랙에서 회원가입 없이 무료로 할 수 있습니다.