Replies: 1 comment
-
10.24 인프라 기준, 한 EC2에 최대로 동시 요청을 보낼 수 있는 사용자 수는 700명 (대기 시간 약 1초) 이다 |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
테스트 단계
성능 테스트 시나리오
시나리오 항목
리뷰미 서비스의 주요 사용자 시나리오를 다음과 같이 정의한다.
시나리오 설정
테스트 스크립트
설정된 vu 수만큼 30초간 생성 후 각 유저들은 다음과 같은 작업을 진행한다.
테스트 목표
위 시나리오를 한 번 실행하는 경우, 최대 몇 명이 실행할 수 있는지 확인한다. 나아가 서버의 반응 속도도 매우 중요한 척도 중 하나이므로, 500ms 안으로 API 호출이 완료되도록 한다.
성능 테스트 흐름
흐름을 그림으로 그리고, 병목이 나타날 수 있는 부분을 먼저 짚어본다
max_connections
만큼 연결을 수립(establish)한다. 3-way handshake가 이 곳에서 이루어진다.max_thread
만큼의 스레드가 생성된다 (혹은min_spare
를 미리 생성한 뒤 여유 스레드에 이를 할당한다).6-1. DB 커넥션 풀에서
Idle
한 커넥션을 가져온다. 이때 커넥션은Active
상태가 된다. 만약 모든 커넥션이Active
라면Pending
상태가 된다.6-2. 커넥션을 토대로 DB와 통신한다. DB 요청/응답을 위해 스레드는 대기한다.
6-3. 커넥션을 모두 사용했다면 반납한다.
Active
였던 커넥션은Idle
상태가 된다.위 흐름 중 1 ~ 3단계는 테스트를 통해 개선할 수 있는 부분이 아니다. 따라서 4 ~ 7단계를 집중적으로 테스트하기 위해 클라이언트, 서버, DB 서버를 모두 같은 VPC에 두어 내부적으로 통신하도록 한다.
최대한 운영 서버와 같은 환경에서 테스트하기 위해서 아래와 같은 사양으로 테스트한다.
실제 운영 서버:
테스트용 서버:
테스트 진행
주어진 시나리오가 존재하므로, 모두 진행되는 데 필요한 최소 시간이 존재한다. 이를 최소화하고, 원활하게 돌아간다면 VU를 높여가면서 반복했다.
테스트를 수행하면서 진행한 최적화
JVM Warm-up
Java 코드는 .class 파일인 바이트코드로 변환되고, 이들이 JAR/WAR로 아카이빙된다. JVM은 이를 읽어들여 기계어로 번역한다.
Interpreter 방식의 JVM은 JIT Compiler를 도입해 적시에 기계어로 만드는 방식을 도입하여 컴파일 기반 언어와의 격차를 줄이게 된다. Oracle에서는 Hotspot이라고 부르며, JDK 1.3부터 반영되어 있다.
어플리케이션이 처음 실행되었을 때에는 코드캐싱 및 최적화된 내역이 없기 때문에 응답 지연이 발생할 수 있다. 따라서 의도적으로 Warm-up을 위한 부하 테스트를 실행했다. 실제로 어플리케이션이 실행된 뒤 처음 수행한 테스트에서는 10~30배의 응답 속도 차이를 보였다.
DB Connection Pool Size
hikaricp.maximumPoolSize
테스트를 수행하면서 DB의 병목이 발생하는 것을 발견했다. DB의 Pending Connection이 늘어남에 따라 대기 시간이 늘어난다고 생각했고, Connection pool size를 늘리면 해결할 수 있을 것이라고 생각했다.
하지만 실제 커넥션이 DB를 사용하는 시간 자체는 크지 않았다. Context switching으로 인한 부하가 더해져 Connection pool size를 키우니 응답 속도가 더 낮아지는 모습을 보였다. OS의 매트릭에서 확인했듯, IO로 인한 프로세스의 Waiting 상태로 이동하지 않았으므로 커넥션 개수를 늘리는 것이 해법이 될 수 없었다.
DB OS의 프로세스 상태. IO로 대기하는 프로세스가 적은 것을 볼 수 있다.
이후 DB Connection pool 개수를 한 개부터 늘려가면서 실험해본 결과, 4개 ~ 5개일 때 가장 좋은 성능을 보였다. 따라서
Hikaricp
의 커넥션 풀 개수를 5개로 결정했다. (커넥션 풀 1 ~ 10에 대한 K6 결과)환경 일치: DB Replication
실제 서버 환경은 DB가 두 대로 나뉘어져 있는 상황이다. 최대한 운영 서버의 환경과 알맞게 설정하기 위해 DB Replication을 테스트 환경에서도 진행했다. R/W 모두 5의 커넥션을 가지도록 했고, 이를 통해 성능 향상을 보았다. (같은 시나리오, 300 -> 500 VU 추가 부하임에도 비슷한 성능과 응답 속도를 보였다.
환경 일치: DB Indexing
운영 환경과 불일치했던 것 중 하나가 인덱싱 적용이었다. 새로운 환경에서 테스트를 진행했기에 해당 부분을 놓쳤고, 이를 추가함으로써 추가 성능 향상을 보았다. (관련 실험 결과): VU 700부터 응답이 느려짐을 확인했다.
Caching: 질문지
조회 로직에서 병목을 확인하고, 이를 해결하기 위해 캐싱을 진행했다. 리뷰미에서는 주어진 질문지에 대해 답변하는 방식으로, 기존에는 사용자가 리뷰를 쓸 때 매번 DB에 질문지를 받아와 응답했다. 질문지는 사용자가 변경하지 않으므로, 변경 가능성이 매우 낮다. 따라서 인-메모리 캐시를 사용해 질문지를 캐싱하고, DB와 통신하지 않도록 진행했더니 VU 700을 안정적으로 응답할 수 있었다.
왼쪽: 캐싱 이전 VU 700, 오른쪽: 질문지 캐싱 후 VU 700
보다 나은 성능을 위해 사용자가 만들지 않는 모든 데이터에 대해서 캐싱을 진행했다. 대표적으로 질문(Question), 선택지(Option), 질문지 내부의 섹션 (Section) 등이다. 캐싱의 범위를 넓혀 보았고, 미미했으나 성능적인 이득을 볼 수 있었다.
Thread Pool Size
Tomcat의 스레드 풀 개수도 성능의 영향이 있을까 하여 조절했다. 특히나 스레드의 개수는 곧 힙 메모리의 크기와 직결되고, 힙 메모리를 많이 쓸 수록 GC가 자주 실행된다. GC가 잦을수록 응답 시간이 느려지므로 이를 최소화해야 한다. 따라서 2GB의 램 용량을 가지는 t4g-small에서는 스레드 풀 크기를 줄이는 것이 낫다고 생각했다. 기본값은 200이다.
thread 값을 20, 50, 100, 200, 300으로 했을 때의 DB 커넥션과 이용 시간, 얻기까지 걸린 시간
스레드 수가 증가할수록 전체적으로 응답 시간의 편차가 커지는 경향을 확인했다. (중앙값, 최댓값, 90%, 95%) 이중 가장 좋은 지표를 가지는 100으로 결정했다.
결론
더 생각해볼 수 있는 점
READ_COMMITTED
로 낮추는 것을 고려한다. 리뷰 내용은 수정할 수 없는 정책이 존재하기 때문에 격리 수준을 한 단계 내리더라도 사용자가 불편함을 겪지 않는다.Beta Was this translation helpful? Give feedback.
All reactions