[refactor] 인덱싱을 통해 출석 과정에서 학생이 강의 정보를 가져오는 시간 단축 #57 #58
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
What is this Pull Request about? 💬
결국 트래픽이 모이는 타이밍은, 출석 당시이다.
아주 짧은 시간 안에
로그인을 하고 (회원가입 할 수도 있다)
수강 신청 승인되었으며, 현재 OPEN 상태인 자신의 강의를 가져온 다음
출석 번호를 보내어 출석 신청을 한다.
전엔 3번 과정을 줄이기 위해 redis를 이용해 DB 접근을 한번 줄였다.
이번엔 2번 과정에서 시간을 줄이기 위해 강의 정보를 가져오는 과정을 인덱싱을 통해 줄이겠다.
자신의 강의이며
수강 신청이 승인 되었고
현재 OPEN 상태인 강의를 가져와야한다.
처음 복합 인덱스를 고려했으나, 다시 공부해 보니 바보 같은 생각이였다.
단일 인덱스로 변경하였고, 동시성 프레임워크를 사용해 직접 성능 차이와 인덱스 용량 테스트 했다.
결론적으로 단일 인덱스를 걸고, 쿼리를 수정했다.
쿼리를 수정하면서 outer join 하나가 줄었고, 사소하지만 객체 변환 과정도 줄어들었다.
2천명의 회원 4천개의 수강 신청 데이터가 있을 때 약 4배 정도의 성능 개선이 있었다.
RDS의 상태에 따라 충분히 결과의 차이가 있을 수 있는 측정이다.
1. 쿼리 변경
자바 동시성 컬렉션인 ThreadPoolExecutor와 CountDownLatch를 이용해 1000건의 동시 요청을 보냈다.
인덱스를 걸고, 쿼리를 아래와 같이 바꿨다.
성능을 테스트 하기 전에 쿼리를 바꾼 부분을 보이겠다.
Before
발행된 쿼리
After
발행된 쿼리
2. 성능 측정 테스트
실제 데이터를 dev용 RDS에 저장했다.
최대한 실제 상황과 유사하게 하고 싶었다.
당연히 RDS의 상태에 따라 성능 차이가 있을 것이다.
그리고 로컬 환경과 EC2 환경의 차이가 있을 것이다.
RDS와의 연결은 EC2가 같은 AZ라서 더 나을 수도 있을것 같다.
2.1 회원이 어느 정도 많은 상황
내 서비스에 4천개의 수강 신청 데이터가 있는 상황을 상상했다. 그러니까, 2천명의 회원이 가입했고, 각자 2개의 수업을 신청한 상황을 가정했다. 출석 상황에 1000명이 몰린 상황을 가정했다.
여러 수업들이 겹치면 1000명도 가능하지 않겠는가?
-> 2천명의 회원 4천개의 수강 신청 데이터가 있을 때, 1000명의 유저가 동시에 요청
2.1.1. 인덱스가 없을 때 : 11.1830초 소요 (1건 ExecutionTime 1.347ms)
2.1.2. memberId 단일 인덱스 : 2.9257초 소요, 인덱스 사이즈 114.688 KB (1건 Execution Time : 0.054ms)
2.1.3. enrollment_state 포함 복합 인덱스 : 2.9068초 소요, 인덱스 사이즈 196.608 KB (1건 ExecutionTime 0.050ms)
두 인덱스를 실제로 사용하는지 확인한 방법 + Execution Time 확인법
두 인덱스의 사이즈를 비교한 방법
2.2 회원이 극단적으로 많은 상황
-> 1만명 회원 10만개의 수강 신청 데이터, 1000명의 유저가 동시 요청 (사실상 수강 신청 데이터 갯수가 중요함)
2.2.1. 인덱스가 없을 때 : 30.5735 초 소요 - 타임아웃, 668건 실패 (1건 ExecutionTime 91.522ms)
2.2.2. memberId 단일 인덱스 : 6.4638 초 소요, 인덱스 사이즈 958.464 KB (1건 Execution Time : 0.052ms)
2.2.3. enrollment_state 포함 복합 인덱스 : 6.4872초 소요, 인덱스 사이즈 1458.176 KB (1건 ExecutionTime 0.046ms)
10만건에서의 인덱스 사이즈 비교
응답 시간 - 둘 다 6.4초대로 비슷했다
3. 결론
다시 공부해보니 enrollment_state는 카디널리티가 매우 낮고, 그마저도 그 안에서 대부분의 값이 주로 찾아야 하는 상태이며, 어차피 한 사람당 수강 신청 갯수가 몇 개 안되기 때문에, 성능적으론 거의 차이가 없었다.
따라서 용량 차이가 1.5배가 나는 것을 감수하고 복합 인덱스를 쓸 필요가 없다고 판단했다. 따라서, memberId에 단일 인덱스를 걸었다.
Key Changes 🔑