MySQL - 연결 오류
오류 내용
1
2
ERROR jdbc.audit - PreparedStatement.execute() SELECT ... FROM ... WHERE ...
java.sql.SQLNonTransientConnectionException: unexpected end of stream (socket was closed by server)
오류 발생 원인
이 오류가 발생하는 원인을 구글링 했을 때, 대체로 MySQL or MariaDB의 wait_timeout 설정이 HikariCP의 maxLifetime 보다 낮게 설정되었을 경우 오류가 발생한다는 의견이 많았다.
이를 이해하기 위해서는 HikariCP의 속성과 wait_timeout 간의 관계에 대해 알아야 한다.
커넥션 풀 Connection Pool
매번 DB에 연결을 시도하는 건 느리고 비효율적이기 때문에, 여러 개의 DB 연결 connection을 만들어두고 필요할 때 재사용하는 방식을 말한다.
대표적인 커넥션 풀 라이브러리에는 HikariCP 등이 있다.
HikariCP
가벼운 용량과 빠른 속도를 가지는 우수한 성능의 JDBC Connection Pool 프레임워크로, Spring Boot에서 기본적으로 사용한다.
주요 설정 속성은 아래와 같다.
설정 | 의미 | 값 예시 (밀리초 단위) |
---|---|---|
maxLifetime | 커넥션 하나를 최대 얼마 동안 유지할지 | 1800000 (30분) |
idleTimeout | 사용하지 않는 커넥션을 얼마 후 제거할지 | 600000 (10분) |
keepaliveTime | 커넥션이 끊어지지 않도록 주기적으로 사용 여부 확인 | 300000 (5분) |
connectionTestQuery | 커넥션이 유효한지 검사할 때 실행할 쿼리 | SELECT 1 |
만약 wait_timeout은 5분으로, maxLifeTime은 30분으로 설정되어 있다고 가정해보자.
이 경우, MySQL은 5분 후 커넥션을 끊었는데, HikariCP는 이 커넥션이 아직 유효하다고 판단하여 연결을 시도하게 되면서 오류가 발생하는 것이다.
내 환경에서의 오류 발생 원인
필자의 서버에서 wait_timeout을 확인한 결과, 기본 설정 시간 (8시간)으로 되어 있었고, maxLifeTime 보다 wait_timeout이 더 크게 설정되어 있는 것으로 보아 설정의 문제는 아니었다.
추가적인 원인을 파악하지 못해 선배님께 여쭤보자 동일한 서버에서 돌아가는 타 서비스에 트래픽이 많아진 것 때문이라는 설명을 들었다.
즉, 타 서비스의 과도한 트래픽이 전체 톰캣 리소스를 고갈시켰고, 결과적으로 내가 관리하는 서비스의 요청도 제대로 DB 연결을 수립하거나 유지하지 못하고 끊기는 경우가 발생한 것이다.
해결?
타 서비스는 내 담당이 아니기도 하고, 전체적인 구조도 알아가는 단계이기 때문에 어떻게 해결해야 할 지 감이 오지 않았다. 우선 선배님께서 내가 관리하는 서비스의 DB를 재시작해주셨더니 전보다는 오류가 덜 발생했다.
내가 생각한 해결 방법
1. 서비스 분리
가장 생각하기 쉬운 방법으로, 남는 서버가 있다면, 서비스 중 하나를 다른 서버로 옮기면 된다!
다만 이 방법은 서버 공간 낭비가 심하고 (내가 관리하는 서비스는 사용자 수가 많지 않기 때문), 번거롭고 (이런저런 설정부터 내부 파일까지 모두 옮겨야 함), 남는 서버가 있어야 한다. (다 돈이다.)
2. 로드밸런싱
부하 트래픽을 분산시켜 서버 부하를 방지한다.
다만, 이 방법은 서버 사양을 높이거나 추가적인 서버가 필요하기 때문에 당장 적용할 수도 없고 쉽게 결정할 수 있는 부분도 아니었다.
결론
이번 오류는 커넥션 풀 설정만의 문제가 아니라, 하나의 톰캣 인스턴스에 여러 서비스를 운영할 때 발생할 수 있는 리소스 충돌이 근본 원인이었다.
특정 서비스에 접속자가 몰리면, 다른 서비스까지 영향을 주는 구조적 한계가 존재함을 알게 되었다.
구글링을 하면 접하게 되는 wait_timeout과 maxLifetime의 불일치가 문제일 수도 있겠지만, 이번 케이스는 시스템 전체 자원 사용량의 영향을 받는 목합적인 문제였고, 단순히 설정을 바꾼다고 해결되지 않는 것도 존재함을 알았다.
회고
- 처음에는 오류 로그만 보고 설정 문제라고 생각했지만 톰캣과 시스템 자원 사용 구조가 원인이었다.
- 이 경험을 통해 서버 전체 관점에서 문제를 분석하는 시야가 중요한 것을 알게 되었다.
- 문제를 해결하지 못한 것은 아쉽지만 원인을 분석하고 가능한 해결책을 정리해보는 과정 자체가 내 성장에 도움이 되었다.
- 다음에 비슷한 상황이 발생하면 더 빠르고 정확하게 대응할 수 있을 것 같다.
참고
향로 님 - Spring Batch에서 socket was closed by server 발생 시
TwoJun 님 - 로드 밸런싱(Load Balancing)의 개념과 특징