개요
이전 포스팅에서 현재 구현된 API의 문제점에 대해서 분석했습니다.
이번 포스팅에서는 오프셋 기반 페이지네이션으로 구현된 로직을 커서 기반 페이지네이션으로 바꾸고 그 과정에서 나오는 문제점, 나아진 점 등을 살펴보도록 하겠습니다.
SQL 작성
현재 커서 기반 페이지네이션을 구현하려면 크게 두가지 정보를 클라이언트에게 요구해야 합니다.
1. cursor 데이터
2. 요청 파라미터
커서(기준)가 될 데이터를 받아온 후 어느 데이터로 정렬을 할지 요청 인자값을 받아와야 합니다. 정렬 기준은 다음과 같습니다.
- 경매 시작일
- 경매 종료일
- 최고 입찰가
- 상한가
unique한 값으로 페이지네이션을 구현하는 것은 쿼리도 간단하고 구현도 복잡하지 않습니다. 중복이 없으니까요.
하지만, 경매 데이터에서 정렬이 될 필드들은 전부 unique 하지 않습니다.
해당 auction table은 경매 시작일과 id를 오름차순하여 정렬한 상태입니다.
더미 데이터로 생성하였기 때문에 실제 데이터라면 이만큼 중복이 많지는 않겠지만 그래도 중복은 존재할껍니다.
해당 부분에서 auction_idx가 248842인 데이터를 커서 데이터로 지정하여 쿼리를 날릴경우 제가 원하는 것은 중복된 데이터라도 그 아래로 10개가 차례대로 나오는 상황입니다.
해당 쿼리를 날려보겠습니다.
사진 1에서 보시다시피 id가 248842 인 데이터 아래로 중복되는 데이터가 4개가 있으나 사진 3에선 제외되었고 그 다음 데이터들이 나오게되는 상황입니다.
만약 실제 API를 제공하는 상황이라면 몇몇 유저의 경매글이 보이지 않는 상황이 발생할껍니다.
그럴 경우를 방지하기 위해 다음과 같은 쿼리로 바꿔봤습니다.
전에는 제외됐던 중복 데이터들이 이제는 보입니다.
이런 중복 문제를 해결하기 위해서는 정렬 기준 필드 이외에 반드시 unique한 값을 같이 사용해야 합니다.
queryDSL 구현
이번에는 queryDSL로 실제 쿼리를 날릴 수 있도록 하겠습니다.
정렬 데이터를 구현하는 부분에서 동적으로 비교하는 커서 필드를 변경해줘야 했기 때문에 메서드로 따로 빼주었고, Enum을 사용하여 직관적으로 볼 수 있게끔 했습니다.
더 깔끔하게 짜고 싶었으나 .. 방법이 있다면 리뷰 부탁드립니다 !
그 결과, 원하는 데이터를 출력할 수 있었습니다.
하지만 아직 응답시간이 3.3s 가량 소요됩니다. 기존에 15초 이상 소요되던 방식에 비하면 많은 개선을 이루었지만, 사용자가 만족스럽게 사용하려면 조금 더 최적화가 필요할 것 같습니다.
실행되는 쿼리의 실행계획을 살펴보겠습니다.
실행 계획
현재 쿼리의 실행 계획입니다.
유저와 기프티콘은 pk로 join하기 때문에 안봐도 되지만 기본적인 드라이빙 테이블인 auction이 인덱스를 타고 있지 않고 Table Full Scan을 타고 있습니다.
또한 filesort 가 일어나고 있는데 이는 레코드가 많아질 수록 성능 저하의 원인이 될 수 있습니다.
그렇기 때문에 order by절의 순서대로 복합인덱스를 생성해보겠습니다.
생성된 후 실행계획을 살펴보겠습니다.
Table Full Scan을 하던 형태에서 Index Range Scan으로 개선되었습니다.
또한 인덱스를 사용하여 filesort를 없애고 적절한 인덱스 생성을 통해 ICP(Index Condition Pushdown) 형태로 최적화 된 것을 볼 수 있습니다.
성능 개선
한번 Postman을 통해서 어느정도 성능 개선이 되었는지 확인해볼까요?
기존에 3.3s 가량 걸리던 작업이 42ms 로 개선되어 성능이 약 70배 향상된 것을 볼 수 있습니다.
이전 포스팅에서 봤던 오프셋 기반 페이지네이션의 구조에서 중간 페이지 정도를 조회 할 경우 약 15 ~ 20초 가량 걸리고 뒷 부분의 페이지를 조회 할 경우 30초까지도 걸리던 작업을 커서 기반 페이지네이션으로 전환하여 구조적 최적화를 진행하였고, 여기서 인덱스 생성을 통하여 최종적인 성능 개선까지 진행했습니다.
custom cursor를 만들어 where 조건절을 더 최적화 할 수도 있겠지만, 현재까지는 충분한 최적화가 진행되었다고 판단하여 여기까지만 진행했습니다.
부하 테스트
nGrinder 라는 툴을 사용하여 동시 사용자 수 100명을 상정하여 부하 테스트를 진행했습니다.
각 컨테이너가 돌아가는 컴퓨팅 자원은 다음과 같습니다.
Application :
CPU : 2core
RAM : 2GB
Database:
CPU : 2core
RAM : 2GB
평균 TPS 약 390, Peak TPS 약 480을 찍은 걸 볼 수 있습니다.
응답 시간의 경우 방금 전 Postman으로 봤을 땐 45ms 정도가 나왔으나 어느정도 부하를 줬을 경우에 평균 250ms가 찍히는 그래프를 볼 수가 있습니다.
사용자 당 트랜잭션 수가 10 정도라고 가정할 때, 평균 400TPS라 가정하면 일반적인 수준의 트래픽에서는 문제가 크지 않을 것으로 판단되나 지속적인 모니터링을 통해 관찰해야 할 것 같습니다.
'Dev > 트러블 슈팅' 카테고리의 다른 글
[conseller] 경매 목록 조회 API 개선 - 문제 상황 파악 (0) | 2024.09.21 |
---|