기록
인덱스 도입
개요
이번에 할 리팩토링은 인덱스입니다..!
사실 처음 리팩토링할 때 부터 생각했던건데 Fetch Join을 했지만, 커서 페이지네이션을 조회할 때 마다 Comment 쿼리가 20개씩 계속 발생을 하죠..! 사실 성능에 큰 영향을 줄거라고 생각은 하지 않지만, 영향이 없지는 않으니까요.. 하하
그때 보면서 든 생각이 커서 페이지네이션 조회할 때 20명 조회하는데 20개의 추가 쿼리가 나가니까 그러면 인덱스 설정해서 속도를 올리면 어느정도 상쇄돼서 괜찮지 않을까? 라는 생각으로 시작을 했습니다..!
페이지네이션 인덱스
먼저 기존 쿼리가 어떻게 나갈지 EXPLAIN을 통해 알아봅시다
EXPLAIN ANALYZE
SELECT * FROM reviews
WHERE is_deleted = false
ORDER BY created_at DESC
LIMIT 20;
삭제되지 않은 리뷰를 생성시간 내림차순으로 20개를 가져오는 쿼리를 설명해달라고 합니다.

이런식으로 어떻게 실행될건지 뜨는데요 리뷰 데이터는 총 1만개를 기준으로 했습니다 !
DB에서의 실행 시간은 27.968ms로 뜨는데 어 생각보다 진짜 빠르네? 라고 생각하긴 했습니다 ㅎㅎ..
이제 인덱스를 걸어보겠습니다 !
CREATE INDEX idx_reviews_active_pagination
ON reviews (created_at DESC, id DESC)
WHERE is_deleted = false;
인덱스 이름은 페이지네이션에 사용하니 idx_reviews_active_pagination로 지었구요
대상 테이블은 reviews
대상 컬럼은 created_at DESC, id DESC
조건은 is_deleted = false인 행에만 인덱스가 적용되게 설정했습니다 !
이제 아까랑 똑같은 쿼리를 실행해서 어떻게 달라졌는지 확인해보겠습니다.

두 번째 줄을 보면 Index Scan이 나오는데 아까 만들어둔 인덱스를 스캔해서 적용되는걸 볼 수 있습니다.
DB에서 실행 시간도 놀라운데요 0.300 ms…?
27.968ms → 0.300ms 약 99% 개선에 성공했습니다 !
단순히 인덱스만으로 DB 조회 성능이 무려 99% 개선이 됩니다. 하지만 성능이 개선된다고 조회에 전부 인덱스 설정을 걸면 안되는게 인덱스는 데이터의 복사본을 만드는 것이기 때문에 추가적인 디스크 공간을 사용해 코스트가 크다는 단점이 있기에 막 걸면 안되겠죠?
그래서 저도 커서페이지네이션 기본 디폴트값인 created_at DESC, id DESC만 걸어뒀습니다 !
댓글 인덱스
두 번째로는 페이지네이션 조회 시 나오는 Comments 쿼리들을 그냥 빨리 조회해서 불러오자라고 생각해서 하게 됐는데요 !
이번에도 똑같이 EXPLAIN을 통해 알아보겠습니다 !
EXPLAIN ANALYZE
SELECT COUNT(*)
FROM comments
WHERE review_id = 'bae783d7-439d-4c58-9788-1827e1a5f3c1' AND is_deleted = false;
review_id가 bae783d7-439d-4c58-9788-1827e1a5f3c1인 삭제가 안된 댓글을 가져오는 쿼리인데요 !

보시면 총 댓글 데이터는 3000개이고, comments를 스캔해서 가져옵니다.
DB에서 실행 속도는 0.760ms로 나오네요 !
이제 인덱스를 걸어보겠습니다.
CREATE INDEX idx_comments_review_active
ON comments (review_id)
WHERE is_deleted = false;
인덱스 이름은 idx_comments_review_active
대상 테이블은 comments
대상 컬럼은 review_id
조건은 is_deleted = false 입니다.
이렇게 설정한 목적은 댓글 수 카운트를 빠르게 하기 위해서구요
review_id 기준 조건 + is_deleted = false 조합에서 빠르게 스캔하기 위해서 인덱스 설정을 했습니다 !
그럼 이제 인덱스 후 같은 쿼리를 보내면

0.066ms..?
0.760ms → 0.066ms로 약 91% 개선된 것을 볼 수 있습니다 !
인덱스를 통해 속도가 엄청 개선된 것을 볼 수 있는데요 ! 그럼 API 차이는 과연 얼마나 날까요?
API 측정
인덱스 전

인덱스 후

분명 스크린샷을 찍었었는데 사라져서 기록으로 보겠습니다 !
인덱스 전 후가 약 300ms 차이로 생각보다 드라마틱한 개선은 없었습니다..
**671ms → 359ms는 약 46%**정도 개선인데 앞에서 90%대 개선을 해서 그런지 성에는 안차는 모습이네요 ㅎㅎ..
Why?
인덱스를 설정해서 성능을 많이 올렸는데 왜 API에서는 개선이 별로 안됐을까요??
저도 궁금해서 ReviewService에 있는 페이지네이션 로직에 로그를 찍어서 확인을 해봤는데요!

왜 이런 결과가 나왔는지 아셨을까요?
커서 페이지네이션은 로직 한 번에 20개씩밖에 조회를 하기 때문에 성능이 크게 향상이 안된걸 볼 수 있습니다.
데이터를 많이 가져온다면 분명 엄청난 차이가 났을텐데 20개라는 적은 데이터만 가져오기 때문에 성능에서 크게 차이가 나질 않았습니다.
하지만 의미가 없지는 않습니다 ! DB 조회 성능 개선에 성공을 했고, 만약 현업이라면 limit의 데이터량은 충분히 변할 수 있다고 생각하기에 유의미한 리팩토링이었던 것 같습니다.
리팩토링을 끝마치며..
팀 프로젝트가 끝나고 성능 개선이나 아쉬웠던 점, 공부하면서 적용해보고 싶었던 것들을 적용해보며 생각보다 다양한 리팩토링을 진행했는데, 리팩토링 전과 후의 성능을 비교해보니 생각보다 차이가 엄청나다는 것을 알 수 있었습니다.
로컬에서는 더미 데이터로 최대 1만개까지만 넣어서 리팩토링 전의 성능이 크게 나쁘지 않았던 것 같은데, 실제 현업에서는 데이터가 엄청 많으니 성능 개선이 필수겠지요.
이번 팀 프로젝트는 기간이 다른 프로젝트때보다 길기 때문에 아마 여러 시도를 할 것 같은데, 많은 데이터를 넣어도 이슈가 없게 구조를 잘 짜야겠습니다 ㅎㅎ 감사합니다.
이번에 진행했던 깃허브 리포지토리입니다 ! https://github.com/Heyaaz/deokhugam