1. 병목 지점

보통 트래픽이 증가하면서 성능 문제가 발생하는데 그 주된 이유는 시스템이 수용할 수 있는 최대 TPS를 초과하는 트래픽이 유입되기 때문이다. 이때, 시스템이 제공할 수 있는 최대 TPS를 높이지 않으면 증가하는 트래픽을 적절히 처리할 수 없다.

 

TPS를 높이려면 먼저 성능 문제가 발생하는 지점, 즉 처리 시간이 오래 걸리는 작업을 식별하는 것이다. 이를 위해선 모니터링 도구를 활용해 실제 실행 시간을 측정해야한다. 적절한 모니터링 도구가 없다면 로그라도 남겨야 한다. 의심되는 코드의 실행 시간을 로그로 남겨두면 나중에 성능 문제가 다시 발생했을 때 개선할 부분을 찾는 데 도움이 된다.

 

실제 실행 시간을 측정해 정확한 지점을 찾아야 하지만, 보통 성능 문제는 DB나 외부 API 연동에서 발생할 가능성이 높다.

 

2. 수직 확장과 수평 확장

사용자가 서비스를 이용하지 못하는 상황을 최대한 줄이기 위해 발견한 성능 문제 원인을 빠르게 적용할 수 있는 개선안을 도출해야한다. 근본적인 해결책(쿼리, 테이블 설계 등)은 먼저 급한 불을 끈 다음에 모색해야 한다. 급한 불을 끄는 방법으론 수직 확장과 수평 확장이 있다.

 

급한 불 끄기로 얻을 수 있는 이점으론 2가지가 있다.

  • 서비스 중단을 막을 수 있다
  • 문제 원인 및 해결 방안 찾을 시간 확보

 

1) 수직 확장(scale-up)

수직 확장은 CPU, 메모리, 디스크 등의 자원을 증가시키는 것을 의미한다. 

  • 더 빠른 CPU로 바꾸거나
  • CPU 코어수를 늘리거나
  • 메모리를 확장하거나
  • 디스크를 HDD에서 SSD로 바꾸거나

수직 확장은 즉각적인 효과를 바로 얻을 수 있지만 트래픽이 계속 증가하는 상황에서 장기적인 대응이 어렵고 무엇보다 비용이 많이 든다는 단점이 있다.

 

2) 수평 확장(scale-out)

수평 확장은 서버를 추가로 늘리는 것을 의미한다. 늘어난 서버로 처리량을 증가시켜 트래픽 증가에 대응할 수 있다.

 

그렇다면 성능 저하엔 무조건 수평 확장을 하는 것이 답일까?

답은 상황에 따라 다르다 이다. TPS를 높이기 위해 무작정 수평 확장을 하는 것은 적절한 대응이 아닐 수 있다.

요점은 실제 병목 지점이 어디인지 파악하는 것이 중요하다는 것이다.

 

만약 DB에서 성능 문제가 발생하고 있는 경우라면 서버 추가로 추가 전 트래픽도 DB가 처리하기 어려운데 늘어난 서버로 더 많은 트래픽을 DB가 처리해야 하므로 DB에 가해지는 부하가 더 커지고 성능 문제는 더 악화된다. 비슷하게 외부 API 연동도 같은 맥락으로 수평 확장으로 인해 상황이 더 악화될 수 있다. 따라서 DB나 외부 API에 성능 문제가 발생하지 않는 범위 내에서만 수평 확장을 해야 효과가 있다.

 

[참고] 로드 밸런서

서버가 2대 이상이면 로드 밸런서가 필요하다. 로드 밸런서는 사용자 트래픽을 각 서버에 골고루 분배해서 한 서버에 사용자 트래픽이 몰리지 않도록 한다. 이를 통해 전체 서버 자원을 효율적으로 활용할 수 있다.

 

로드 밸런서가 트래피을 알맞게 분산시키기 위해 사용하는 방식은 크게 정적인 방식과 동적인 방식으로 나뉜다.

  • 정적인 방식
    • 라운드 로빈 : 클라이언트 요청을 각 서버에 순차적으로 분배하는 방식.
    • IP 해시 방식 : 클라이언트 IP 주소를 해시한 값을 기반으로 요청 전달 서버 결정하는 방식. IP 해시값은 동일하기 때문에 동일한 클라이언트는 항상 같은 서버로 연결되도록 해준다
  • 동적인 방식
    • 서버의 현재 상태에 따라 트래픽을 분산하는 방식
    • 트래픽이 적은(연결 수가 더 적거나 응답 시간이 더 짧은) 서버에 요청을 보내는 형태로 동작

 

3. DB 커넥션 풀

서버에서 데이터에 접근하기 위해선 DB에 연결이 필요한데, 이 연결은 네트워크 통신을 통해 이뤄진다.

DB 연결/종료를 위해 걸리는 시간은 전체 응답 시간에 영향을 주는데, 연결 시간은 보통 0.5초 에서 1초로 그리 길지 않은 시간인 것 같지만 10ms 같이 짧은 쿼리 실행만 필요한 경우라면 상대적으로 매우 긴 시간이고 전체 응답 시간을 확 증가하게 하는 요인이 된다.

 

이 문제를 해결하기 위해 DB 커넥션 풀을 사용한다. DB 커넥션 풀은 DB에 연결된 커넥션을 미리 생성해서 보관하는데, 애플리케이션은 DB커넥션 풀에서 커넥션을 가져와 사용하고, 작업이 끝나면 다시 풀에 반환한다. 커넥션 풀을 사용하면 이미 연결된 커넥션을 재사용하기 때문에 응답 시간이 줄어드는 장점이 있고, 서버 개발에서 DB 커넥션 풀 사용은 필수라고 할 수 있다.

 

DB 커넥션 풀이 제공하는 설정은 다음과 같다.

  • 커넥션 풀 크기(최소/최대 크기)
  • 커넥션 대기 시간
  • 커넥션 유지 시간(최대 유휴 시간, 최대 유지 시간)

 

1) 커넥션 풀 크기

커넥션 풀 크기는 커넥션 풀에 미리 생성해둘 커넥션 개수를 지정하는 설정이다. 커넥션 풀 크기는 커넥션 풀 설정에서 가장 중요한데, 크기를 잘못 설정할 경우 커넥션을 위해 대기하는 요청의 수가 증가하게 되고 결과적으로 성능 저하로 이어질 수 있기 때문이다.

 

 

커넥션 풀 대기 시간을 줄이기 위해선 전체 응답 시간과 TPS를 고려해 커넥션 풀 크기를 지정해야 한다.

1초에 처리할 수 있는 요청 수 = (1초/쿼리 실행시간) * (커넥션 풀 크기)

 

  • 커넥션 풀 최소 크기, 최대 크기
    • 트래픽의 패턴에 따라 커넥션 풀 최소/최대 크기 설정값이 달라질 수 있다
    • 트래픽이 적은 시간대에는 최소 크기로 유지하고
    • 트래픽이 높은 시간대에는 최대 크기로 확장해서 커넥션 개수를 필요한 만큼만 유지할 수 있다
    • 트래픽이 순간적으로 급증하는 패턴이라면, 커넥션을 만들 수 있을만큼 다 만들어두면 처리량을 높여 빠른 대응을 할 수 있다

 

주의할 점은 커넥션 풀 크기를 무턱대고 늘리면 안된다는 것이다. DB 상태를 보고 늘려야 한다. DB 서버의 CPU 사용률이 80%에 육박하는 상황에서 커넥션 풀 크기를 늘리면 DB에 가해지는 부하가 더 커져 쿼리 실행 시간이 급격히 증가할 수 있다. 따라서 DB 서버의 상태를 면밀히 확인한 후에 수평 확장을 진행해야 한다.

 

4. 커넥션 대기 시간

대기 시간은 풀에 사용할 수 있는 커넥션이 없을 때 커넥션을 얻기 위해 기다릴 수 있는 최대 시간을 의미한다.

지정된 대기 시간 안에 커넥션을 구하지 못하면 DB 연결 실패 에러가 발생한다.

커넥션 대기 시간은 응담 시간에 영향을 주므로, 응답시간이 중요한 서비스의 경우 커넥션 대기 시간을 가능한 한 짧게 설정해야 한다.

보통 0.5초에서 3초 이내로 지정하는 걸 추천한다.

 

Q. 성공 응답이 올 때까지 대기 vs 짧은 대기시간으로 응답 에러가 발생

대기시간을 짧게 설정하면 서버에 문제가 없음에도 응답 에러가 발생할 수 있다. 하지만 에러가 발생하는 것보다는 긴 시간 무응답 상태를 유지하는 것이 서버에 부하를 가할 수 있어 되도록이면 빨리 에러를 응답하는 게 더 좋은 방법일 수 있다.

 

대기 시간이 길어지면 클라이언트가 기존의 대기 요청을 취소하고 재요청을 하는 경우가 발생할 수 있다. 이 때 클라이언트가 요청을 취소했다 하더라도 서버에서는 해당 요청이 여전히 대기 중이기에 새로 기존 대기 요청 수에 새로운 요청 수만 증가되고, 이는 서버가 동시에 처리해야하는 요청의 수가 증가해 서버에 가하는 부하가 증가하고 속도 저하가 발생할 수 있다.

 

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

 

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

 

커넥션이 사용되지 않는 시간이 길어지면 연결이 끊길 수 있고, 끊긴 커넥션을 사용할 경우 에러가 발생한다. 연결이 끊긴 커넥션 사용으로 인한 에러를 방지하기 위해 커넥션 풀은 다음 2가지를 제공한다.

  • 최대 유휴 시간 지정
    • 사용되지 않는 커넥션을 풀에 유지할 수 있는 최대 시간을 의미
    • ex. 최대유휴시간 = 30분, 30분 이상 사용되지 않은 커넥션은 종료되고 풀에서 제거됨
  • 유효성 검사 지원
    • 커넥션의 정상 사용 가능 여부를 확인하는 절차
    • 연결이 유효하지 않은 커넥션을 식별하고 풀에서 제거 가능하다
  • 최대 유지 시간 
    • 커넥션이 생성된 시점부터 연결을 유지하는 시간
    • 설정 최대 유지 시간이 지나면 커넥션이 유효하더라도 커넥션을 닫고 풀에서 제거된다.

+ Recent posts