웹 애플리케이션을 운영하다 보면 에러 로그로는 식별 할 수 없는 잠재적인 이슈가 발생합니다 대체로 애플리케이션내의 오류가 명확히 확인이 되지 않은 상태에서 Out of memory가 발생 하거나, DB 서버에서부터 발생한 장애로 인해 웹 서버가 뻗어 버리는 경우가 해당합니다. 결론부터 말하면 이러한 문제는 대부분 애플리케이션의 비지니스 로직의 오류가 아닌 WAS와 DB 서버간의 이슈로 DB Connection Pool에 대한 이해가 필요한 내용입니다.
왜 이러한 문제가 발생하게 될까?
먼저, 웹 어플리케이션을 지탱하는 WAS에서 DB 서버에 접근을 시작하고 데이터를 가져오기까지의 단계에서 가장 비용이 드는 부분이 어디일까? 과정은 다음과 같습니다.
- DB 서버 접속을 위해 JDBC 드라이버를 로드합니다.
- DB 접속 정보와 DriverManager.getConnection() Method를 통해 DB Connection 객체를 얻습니다.
- Connection 객체로 부터 쿼리를 수행하기 위한 PreparedStatement 객체를 받습니다.
- executeQuery를 수행하여 그 결과로 ResultSet 객체를 받아서 데이터를 처리합니다.
- 처리가 완료되면 처리에 사용된 리소스들을 close하여 반환합니다.
위의 예제를 통해서 Database에서 원하는 데이터를 얻어 오기 까지의 과정에서 처리 시간이 0.1초가 소요된다면 어느 과정에서 비용이 가장 많이 발생할까? 가장 느린 부분은 웹 서버에서 물리적으로 DB서버에 최초로 연결되어 Connection 객체를 생성하는 부분이다.
웹 애플리케이션은 HTTP 요청에 따라 Thread를 생성하게 되고 대부분의 비지니스 로직은 DB 서버로 부터 데이터를 얻게 된다. 만약 위와 같이 모든 요청에 대해 DB접속을 위한 Driver를 로드하고 Connection 객체를 생성하여 연결한다면 물리적으로 DB 서버에 지속적으로 접근해야 될 것이다.
이러한 상황에서 DB Connection 객체를 생성하는 부분에 대한 비용과 대기 시간을 줄이고, 네트워크 연결에 대한 부담을 줄일수 있는 방법이 있는데 바로, DBCP(Database Connection Pool)를 이용하면 이러한 문제를 해결할 수가 있다.
커넥션 풀: DBCP(Database Connection Pool)
DBCP 란?
- 웹 컨테이너(WAS)가 실행되면서 DB와 미리 connection(연결)을 해놓은 객체들을 pool에 저장해두었다가 클라이언트 요청이 오면 connection을 빌려주고, 처리가 끝나면 다시 connection을 반납받아 pool에 저장하는 방식을 말합니다.
- DBCP은 HTTP 요청에 매번 위의 1-5의 단계를 거치치 않기 위한 방법입니다. Connection Pool을 이용하면 다수의 HTTP 요청에 대한 Thread를 효율적으로 처리할 수 있게 됩니다.
커넥션 풀(DBCP) 원리
- 웹 컨테이너(WAS)가 실행되면서 connection 객체를 미리 pool에 생성해 둡니다.
- HTTP 요청에 따라 pool에서 connection객체를 가져다 쓰고 반환합니다.
- 이와 같은 방식으로 물리적인 데이터베이스 connection(연결) 부하를 줄이고 연결 관리합니다.
- pool에 미리 connection이 생성되어 있기 때문에 connection을 생성하는 데 드는 요정 마다 연결 시간이 소비되지 않습니다.
- 커넥션을 계속해서 재사용하기 때문에 생성되는 커넥션 수를 제한적으로 설정가능 합니다.
동시 접속자가 많을 경우
- 위에 커넥션 풀 설명에 따르면, 동시 접속 할 경우 pool에서 미리 생성 된 connection을 제공하고, 없을 경우는 사용자는 connection이 반환될 때까지 번호순대로 대기상태로 기다립니다.
- 따라서 WAS에서 커넥션 풀을 크게 설정하면 메모리 소모가 큰 대신 많은 사용자가 대기시간이 줄어들고, 반대로 커넥션 풀을 적게 설정하면 그 만큼 대기시간이 길어진다.
Connection Pool 구현체: Connection Pool 라이브러리 이용
WAS가 실행 될 때 애플리케이션에서는 Connection Pool 라이브러리를 통해 Connection Pool 구현체를 사용할 수가 있는데, Apache의 Commons DBCP가 오픈소스 라이브러리로 제공되고 있습니다.
Connection Pool 구현체의 역할:
- WAS가 실행되면서 미리 일정량의 DB Connection 객체를 생성하고 Pool 이라는 공간에 저장해 둡니다.
- HTTP 요청에 따라 필요할 때 Pool에서 Connection 객체를 가져다 쓰고 반환합니다.
- 이와 같은 방식으로 HTTP 요청 마다 DB Driver를 로드하고 물리적인 연결에 의한 Connection 객체를 생성하는 비용이 줄어들게 됩니다.
더 구체적으로 살펴보자:
Connection Pool를 효율적으로 관리하기 위한 값들이 존재하는데 이러한 값들은 DBCP 구현체를 제공하는 라이브러리를 통해 설정 할 수가 있습니다.
ValueDescription
maxActive | 동시에 사용할 수 있는 최대 커넥션 개수 |
maxIdle | Connection Pool에 반납할 때 최대로 유지될 수 있는 커넥션 개수 |
minIdle | 최소한으로 유지할 커넥션 개수 |
initialSize | 최초로 getConnection() Method를 통해 커넥션 풀에 채워 넣을 커넥션 개수 |
아래의 그림을 살펴보면 Pool에 Connection 을 대기(Idle) 상태로 두었다가 실제 사용하는(Active) 모습인데 DBCP를 관리하기 위한 올바른 설정 값에 대해 살펴 보도록 합시다.
Value 설정
maxActive 값은 DBMS의 설정과 애플리케이션 서버의 개수, Apache, Tomcat에서 동시에 처리할 수 있는 사용자 수 등을 고려해서 설정해야 합니다.
- DBMS가 수용할 수 있는 커넥션 개수를 확인한 후에 애플리케이션 서버 인스턴스 1개가 사용하기에 적절한 개수를 설정합니다.
- 사용자가 몰려서 커넥션을 많이 사용할 때는 maxActive 값이 충분히 크지 않다면 병목 지점이 될 수 있습니다.
- 반대로 사용자가 적어서 사용 중인 커넥션이 많지 않은 시스템에서는 maxActive 값을 지나치게 작게 설정하지 않는 한 성능에 큰 영향이 없습니다.
커넥션 개수와 관련된 속성은 다음과 같은 조건을 논리적 오류가 없게 설정합니다.
- maxActive >= initialSize
- maxIdle >= minIdle
- maxActive = maxIdle
initialSize와 maxActive, maxIdle, minIdle 항목을 동일한 값으로 통일해도 무방합니다.
- 커넥션 개수와 관련된 가장 중요한 성능 요소는 일반적으로 커넥션의 최대 개수입니다.
- 4개 항목의 설정 값 차이는 성능을 좌우하는 중요 변수는 아닙니다.
WAS의 Thread 수와 Connection Pool 수의 관계
WAS에서 설정해야 하는 값이 굉장히 많지만, 그 중 가장 성능에 많은 영향을 주는 부분은 Thread와 Connection Pool의 개수입니다.
이들 값은 직접적으로 메모리와 관련이 있기 때문에, 많이 사용하면 할수록 메모리를 많이 점유하게 됩니다. 그렇다고 반대로 메모리를 위해 적게 지정한다면, 서버에서는 많은 요청을 처리하지 못하고 대기 할 수 밖에 없습니다.
그렇다면.. DB Connection Pool 은 어떻게 관리하면 좋을까?
가장 좋은 방법은 애플리케이션을 실제 운영할 시스템 환경에서 성능 테스트를 진행하는 것입니다. 성능 테스트를 진행하면서 지금까지 살펴본 DBCP에 대한 원리와 설정을 위한 값들을 복기하면서 시스템 환경에 최적화된 값을 찾아 내는 것이 좋습니다.
애플리케이션에 대한 모든 요청이 DB에 접근하는 것은 아니기 때문에 WAS Thread 수는 DB Connection Pool의 갯수보다 더 적게 설정되어 있으면 효율적이지 않습니다.
Connection Pool은 시스템의 환경에 따라 다르지만 보통 40~50개로 지정하면 Thread는 이보다 10개 정도 더 지정하는 것이 바람직합니다. 하지만 최적의 성능의 위해서는 실제 요청이 얼마나 들어오는지 파악하는게 중요하며 가장 좋은 방법은 앞서 말한것 처럼 성능 테스트를 통해 최적화된 값을 구해야합니다.
참고
'Study: ComputerScience(CS) > CS: Database(SQL)' 카테고리의 다른 글
[DB] MySQL 아키텍처 (feat. Real MySQL) (2) | 2022.11.18 |
---|---|
[DB] RDB 트랜잭션 (feat. 트랜잭션 격리 수준) (0) | 2022.11.08 |
[DB] DB 데이터 dump하는 방법 : MySQL 데이터를 export, import 하자! (feat. mysqldump) (0) | 2022.09.09 |
[DB] 데이터베이스 용어정리 (0) | 2022.04.16 |