DB Connection Pool

서론

커넥션 풀 설정의 의미, 왜 중요한지 그리고 HikariCP와 DBMS와의 관계를 정리해본다.

커넥션 풀

img_1.png

DB에 쓰기를 하기 위한 과정을 다음과 같이 3단계로 설명할 수 있다.

  1. DB 연결
  2. 쿼리 실행
  3. 실행이 완료되면 연결 종료

이 과정에서 1번 단계에서 네트워크 연결을 하는데 시간이 꽤 거릴 수 있다. DB와 클라이언트의 연결 과정은 TCP 기반이고 TCP의 연결 지향적 특성 때문에, 연결 때 3-way, 닫을 때 4-way handshake가 필요하다. 이런 과정때문이나 네트워크 문제 발생 시 DB의 응답 시간에 큰 부분 차지하는 경우가 존재 할 수 있다는 의미다.

이런 네트워크 지연 문제를 해결하기 위한 방법이 '커넥션 풀(Connection Pool)' 기법인 것.

커넥션 풀은 DB에 연결된 커넥션을 미리 생성하여 보관한다. 애플리케이션은 DB 작업이 필요할 때 풀에서 커넥션을 가져와 사용하고 다시 반환한다. 커넥션 풀을 쓰면 이미 연결된 커넥션을 재사용하기에 네트워크를 통한 DB 커넥션을 열고 닫는 시간을 단축할 수 있는 장점이 있다.

커넥션 풀의 중요한 설정

커넥션 풀은 다양한 설정을 할 수 있다. 백엔드 서버와 DB 서버 각각에서의 설정 방법을 잘 알고 있어야한다. DB 서버는 MySQL을 기준으로, 백엔드 서버의 커넥션 풀은 Hikari CP 기준으로 설정값을 설명해본다.

[MySQL 기준]

  • max_connections - 클라이언트와 맺을 수 있는 최대 커넥션 수
  • wait_timeout - 커넥션이 inactive(idle)할 때 다시 요청이 오기까지 얼마의 시간을 기다린 뒤에 close할 것인지 결정
    • 비정상적 커넥션 종료, 커넥션 다 쓰고 미반환, 네트워크 단절 등의 경우 대응
    • 비정상 리소스 점유 예방

[Hikari CP 기준]

  • minimumIdle - 풀 에서 유지하는 최소한의 idle 커넥션 수(in-use 동안은 여기서 제외됨)
  • maximumPoolSize - 풀이 가질 수 있는 최대 커넥션 수, idle, active(in-use) 커넥션 합쳐서 최대 수
  • maxLifeTime - 풀에서 커넥션의 최대 수명, 이거 넘기면 idle경우 풀에서 바로 제거, active경우 풀에 반환된 뒤 제거
    • 이건 db의 connection time out 보다 몇 초 짧게 설정하라고 가이드. 만약 같다면 끝날 시점에서 통신 시차로 오류 발생 가능성 존재하기 때문
  • connectionTimeOut - 풀에서 커넥션 받기 위한 대기 시간
  • idleTimeout - 유휴 상태(Idle)로 남아있을 수 있는 최대 시간

그 중 DBCP에서 중요한 설정은 다음과 같다.

  • 커넥션 풀 크기(최소 크기, 최대 크기)
    • maximumPoolSize
  • 커넥션 대기 시간(풀에 커넥션이 없을 때, 커넥션 얻기 위한 대기 시간)
    • connectionTimeOut
  • 커넥션 유지 시간(최대 유휴 시간, 최대 유지 시간)
    • idleTimeout, maxLifeTime

커넥션 풀 크기

미리 생성해 둘 커넥션의 개수를 지정하는 설정. 이 설정이 가장 중요하다.

왜냐하면 서버는 주로 DB와 통신하는데 이 DB를 연결하는 커넥션 풀 크기를 잘못 설정하면 성능에 큰 영햐을 주기 때문이다. 이해를 돕기위해 다음과 같은 상황을 가정한다.

  • 커넥션 풀 크기 = 5
  • 한 요청에 쿼리 실행 1초 소요(계산 용이성 위해 데이터 전송 시간 무시)

이때 만약 요청이 6개 동시에 들어오면 마지막 요청은 다른 요청이 커넥션을 다 쓰고 반환할 때 까지 대기해야한다.

커넥션을 얻기 위한 대기시간을 줄이려면 전체 응답 시간과 TPS를 고려해 커넥션 풀 크기를 조절해야한다.

커넥션 풀 크기가 5이고 한 요청의 쿼리를 처리하는데 0.1초가 걸린다 가정하자. 이때 1초에 처리할 수 있는 요청은 50(1/0.1초 * 5)가 된다. 즉, 동시에 50개 요청이 와도 모두 1초 내에는 끝난다.

패턴에 따라 풀의 최소 크기를 조절할 수도 있는데, 트래픽이 순간적으로 급증하는 패턴이라면 최소 크기를 최대 크기에 맞추는게 좋다. 트래픽이 점진적으로 증가시에는 DB연결 시간이 성능에 큰 영향을 주지 않지만, 급증할 경우는 DB 연결 시간도 성능 저하의 주요 원인이 될 수 있기 때문이다.

풀의 크기를 늘리면 처리량이 늘겠지만 그렇다고 생각없이 늘리면 안된다. 이렇게 늘리며 DB 서버의 CPU에 과부하(사용올 80% 정도)가 생기면 쿼리 실행 시간이 늘어날 수도 있다. 이런 때는 풀의 크기를 늘리기 보다는 오히려 크기를 유지하거나 줄여서 DB 서버의 CPU 사용율을 줄여줘야한다.

커넥션 대기 시간

대부분 커넥션 풀은 대기 시간 설정이 가능하다. 대기 시간이란 풀에 사용할 수 있는 커넥션이 없을 때 이를 얻기 위해 기다릴 수 있는 최대 시간을 의미한다. 이 대기 시간을 넘어서면 연결 실패 에러가 발생한다.

이 대기 시간만큼 응답 시간도 당연히 늘어나게된다. 참고로 Hikari CP의 기본 대기 시간은 30초이다. 응답 시간이 중요한 서비스는 이 대기 시간을 짧게 설정하도록 하자.

대기 시간을 짧게한다면 연결 오류같은 에러 응답이 사용자에게 갈 수 있긴하다. 하지만 대기 하느라 오랜시간동안 무응답을 하기 보단 에러라도 반환하는게 났다. 사용자 경험도 그렇고, 에러 응답을 즉각적으로 해줘야 서버의 부하가 증가하는 것도 방지 가능하기 때문이다.

예를 들어 풀 크기가 10이고 대기시간이 30초라 가정해보자. 동시 요청이 30개 왔는데 순간적으로 DB 서버에 부하가 걸리면서 쿼리 실행 시간이 10초로 늘었다. 이 시점 각 요청은 다음 상태가 된다.

  • 요청 10개는 풀에서 커넥션 확보하여 쿼리 실행
  • 요청 20개는 커넥션 확보 실패하여 대기 상태

이때! 대기하는 사람 중 절반이 5초만에 요청을 취소한다면? 먼저 확보한 10개의 요청은 쿼리 실행 시간 10초 중 5초를 실행한 상태이고, 커넥션 못 구한 요청 20개는 아직도 대기 상태이며 대기시간 30초 중 5초가 흐른 상황이다. 이후 이전에 요청을 신청한 사람들이 또 10개의 재요청을 했는데 이건 또 대기 상태를 가지게 된다.

즉, 클라이언트가 요청을 취소해도 서버는 그게 취소됬는지 모르기에 그대로 하게되고, 이 시점에 대기 중인 요청의 수는 30개가 되버린다.

이와 같이 몇 초만에 요청을 취소하고 재요청하면 서버가 처리해야되는 요청의 수는 증가한다.

만약 대기 시간을 짧게했다면 오류 응답을 주므로 요청 자체는 완결되어 재요청에 의한 요청 누적은 어느정도 방지가 가능한 것.

이처럼 대기 시간을 짧게하면 서버 부하를 일정 수준으로 유지할 수 있으며, 서버를 안정적으로 운영하는데 도움이 된다.

최대 유휴 시간, 최대 유지 시간, 유효성 검사

요청이 없다면 커넥션 풀도 사용되지 않게 된다. 이때 주의할 점이 커넥션이 사용되지 않는 시간이 길어지면 연결이 끊어질 수 있다는 점이다.

MySQL 같은 DB는 일정 시간동안 클라이언트와의 상호작용이 없으면 자동으로 커넥션을 끊는 기능을 제공한다.

이때, DB와 연결이 끊어진 커넥션을 사용하면 당연히 에러가 발생한다. 이런 연결 끊어짐으로 인한 에러 방지를 이해 커넥션 풀은 다음 2가지 기능을 제공한다.

  • 최대 유휴 시간 지정
  • 유효성 검사

최대 유휴 시간 지정은 사용되지 않는 커넥션을 풀에 유지할 수 있는 최대 시간을 의미한다. 만약 30분으로 설정하면 30분 이상 사용되지 않은 커넥션은 종료되어 풀에서 제거가 되는 것. 이 시간을 DB에 설정된 비활성화 유지 시간보다 짧게 하면 DB가 먼저 연결 끊기 전에 풀에서 해당 커넥션을 제거 할 수 있다.

유효성 검사는 커넥션이 정상적으로 사용할 수 있는 상태인지 여부를 확인하는 검사다. 풀의 구현 방식에 땨라 커넥션 풀에서 커넥션 가져올 때 이런 유효성 검사를 하거나 주기적으로 풀의 커넥션들을 검사할 수 있다. 이 과정을 통해 비유효한 커넥션 제거가 가능한것이다. 유효성 검사를 위해 커넥션 풀에 SELECT 1 FROM dual, SELECT 1 같은 쿼리를 실행하곤 한다.

번외로 커넥션 풀이 제공하는 설정은 최대 유지 시간이다. 이 값이 4시간으로 되있으면 커넥션은 생성 시점으로부터 최대 4시간만 유지된다. 4시간이 지나면 커넥션이 유효하든 말든 커넥션 풀 구현체가 그냥 커넥션 닫고 풀에서 삭제해버린다.

최대 유휴 시간, 최대 유지 시간 같은 설정은 DB 설정에 맞추어 적절한 값을 도출하자.

적절한 커넥션 수 찾기위한 팁 by 쉬운코드

  1. 모니터링 환경 구축(서버 리소스, 서버 스레드 수, DBCP 등)
  2. 백엔드 시스템 부하 테스트
  3. request per second, avg response time 확인
  4. 백엔드, DB 서버의 CPU, MEM 등 리소스 사용률 확인
  5. thread per request모델이라면 active thread 수확인
  6. DBCP의 active connection 수 확인
  7. 백엔드 서버 수 고려해서 DBCP의 max pool size 결정

쉬운코드 DBCP영상의 후반부에 더 자세히 설명해주시니 관심있으시면 꼭 참고해보시길 바란다.

참고자료