[my-klas] 병목 해결하기

woodyn·2021년 5월 3일
0

문제 정의

저번 실험 결과를 개선해보고자 한다.
문제를 진단하기 위해서 수집한 Metrics를 Grafana로 시각화했고, 원인으로 지목할만한 부분을 살펴봤다.

의심되는 부분 #1: 애플리케이션은 충분히 빨랐다

클라이언트가 측정한 평균 응답 시간은 3253ms인 반면, 애플리케이션에서는 아무리 길어도 500ms가 채 되지 않는다.
양쪽에서 측정한 응답 시간이 6배 이상 차이나고 있다. 클라이언트와 애플리케이션 중간의 어딘가에서 병목이 일어나고 있는 걸까?

의심되는 부분 #2: DB Connection을 오래 기다린다

HikariCP에서 Connection을 획득하기 위해 기다리는 전체 평균 시간이 333ms이다.
그런데 애플리케이션에서 요청을 처리하는 전체 평균 시간이 352ms이다.
결국 애플리케이션의 요청 처리 시간의 대부분은 DB Connection을 얻기 위해 대기하느라 보낸 것이다.
이를 개선할 수 있지 않을까?

클라이언트와 애플리케이션 사이 병목 원인 찾기

의심되는 부분 #1부터 생각해보기로 했다.
클라이언트와 애플리케이션의 응답 시간 차이가 큰 원인이 무엇이 있을지 정리해봤다:

  • 클라이언트 문제? (e.g. 잘못된 gatling 설정)
    • 혹시나 빠트린게 있을까 싶어 gatling 공식 문서를 쭉 훑어봤지만, 모두 제대로 설정해둔 상황이었다.
    • 시뮬레이션 리포트를 확인해봐도 이상한 부분은 없었다.
    • 애초에 클라이언트가 요청을 2초 넘게 기다렸다가 받을 이유가 없다.
  • 네트워크 문제? (e.g. 대역폭 제한)
    • c5a.2xlarge EC2 인스턴스를 사용 중인데, 네트워크 대역폭이 최대 10Gbps이다.
    • EC2 콘솔에서 확인해봤지만 전송량이 커봤자 3Mbps밖에 안 된다.
  • 서버 문제? (e.g. 요청 큐에서 대기)
    • 음, 이건 그럴 듯 하다. 요청이 큐에 가득 차면, 애플리케이션이 아무리 빨라도 늦게 처리되지 않을까?
    • 이를 해결하기 위해서는 병렬화가 필요하다. Tomcat의 스레드 수를 늘려보자.

Tomcat 스레드 풀 크기 조정해보기

Tomcat의 기본 스레드 풀 최대 크기는 200인데, 더 높이면 오히려 성능이 떨어졌고, 더 낮추면 소폭 향상됐다.

  • 하드웨어 성능 문제? (e.g. 부족한 vCPU 수)
    • 스레드 개수를 늘렸을 때 성능이 개선되어야 하지만, CPU가 부족해서 병렬성이 떨어졌을 수 있다.
    • 이를 해결하기 위해서는 vCPU 수를 늘려야 한다. 인스턴스 타입을 c5a.4xlarge로 바꿔보자.

vCPU 수 늘려보기

전반적으로 나아지긴 했으나, 그렇게 큰 차이는 아니다.

슬슬 다른 방안이 없으니 백트래킹을 해보자.
부족한 병렬화가 원인이지만 사실 이를 개선하는 데엔 한계가 있고(하드웨어 성능을 높이는 일은 비용이 크므로), 애플리케이션의 응답 속도를 더 개선하는 방향이 옳을 지도 모른다.
처음으로 돌아가서 의심되는 부분 #2부터 해결해보자.

DB Connection 경쟁 완화하기

HikariCP의 Connection을 얻는 데에 오랜 시간이 걸리는 이유를 정리해봤다:

  • DB에서의 Lock에 의한 Blocking?
    • Student와 Lecture에 x-lock을 걸고 있으므로 가능성이 있다.
    • 다만, 쿼리를 개선하기보다 다른 병목을 우선적으로 해결하고자 한다.
  • 적은 Connection에 의한 경쟁?
    • Tomcat 스레드는 200개인데, HikariCP의 기본 풀 크기는 최대 10이다. 20:1 경쟁이 생길 수 있다.

두 가능성 모두 HikariCP의 풀 크기를 늘리는 방법으로 개선을 확인할 수 있다.

확연한 차이를 보였다!
크기를 5만 키워도 마치 숨통이 놓이듯 400ms가 줄어들었다.
병렬화 문제 이전에 DB Connection에 의한 병목으로 좋은 성능을 낼 수 없었던 것으로 보인다.

최적의 조합 찾기

만약 Tomcat 스레드 수와 HikariCP의 Connection 수 차이에 의한 경쟁이 문제였다면, 최적의 성능을 보이는 조합이 분명 존재할 것이다.
그 최적의 조합을 찾기 위해 두 변수를 조작하여 클라이언트 기준 평균 응답 시간을 측정해보았다.

이를 통해 다음과 같은 사실들을 알 수 있다:

  • Tomcat 스레드 풀 크기가 작으면 병렬성이 떨어진다.
    • 50 미만에서 응답 시간이 크게 커진다.
    • 애플리케이션 응답 시간은 짧은 것으로 보아, 병렬적으로 작동하지 못해 생긴 문제라고 말할 수 있다.
  • HikariCP 풀 크기가 작으면 병목이 생긴다.
    • 30 미만에서 응답 시간이 크게 커진다.
    • Lock에 의해 Connection이 자주 blocking되고, 사용할 수 있는 Connection 수가 적어져 획득에 대한 경쟁을 키운 것으로 보인다.
  • 풀 크기가 과하면 좋지 않다.
    • 스레드가 너무 많으면 문맥교환의 오버헤드로 성능이 떨어진다.
    • Connection도 마찬가지로 너무 많으면 DB에서 많은 스레드가 필요하고, 결국 성능이 떨어진다.

결론

병목의 모든 원인을 밝혔다. Tomcat 스레드 풀 최대 크기와 HikariCP 풀 최대 크기를 적절히 조절해야 한다.
적당히 좋은 결과를 보인 100과 40으로 설정하여 추가적인 개선을 진행해보기로 한다.

profile
🦈

0개의 댓글