사이드프로젝트 간 인덱스를 이용한 쿼리 조회 성능 사례를 정리하고자 한다.
상황
위는 현재 ERD 상태 이고, 사용자(user)와 봉사 모집글(recruitment)에 이미지를 업로드 할 수 있고 추가로 로그 테이블도 존재하기 때문에 이미지 테이블을 분리시켜 놓았다.
봉사 모집글의 상세 정보를 조회하는 상황에서 참여자 리스트의 정보(닉네임, 이미지 등)가 필요하였다.
JPA를 사용하는 상황에서 봉사 모집글의 참여자 정보는 Fetch Join, EntityGrapth 등의 기술로 가져올 수 있었지만,
참여자의 이미지는 함께 가져오지 못해 별도의 쿼리를 사용했다.
아래는 문제의 비지니스 로직의 부분이다.
참여자들의 수(N)만큼 이미지 쿼리가 발생했고, 초기에는 대수롭지 않게 넘겼다.
테스트 케이스 실행 중 방대한 양의 쿼리를 콘솔로 보게 되었고, 간단히 만건의 더미 데이터로 테스트해봤더니
....20분...30분... 언제 끝나?!....
하나의 쿼리로
쑤레기 같은 로직이라 생각했고, 참여자들의 정보를 하나의 쿼리로 가져오도록 쿼리를 바꾸어 보았다.
테이블간 left, inner join을 사용하였고, 이미지와 사용자간 엔티티 매핑이 없어 Spring Data JPA의 클래스 기반 Projection 를 사용하여 참여자 정보를 바로 가져오도록 하였다.
10개의 봉사 모집글과 각 모집글 별 만명의 참여자 더미 데이터로 테스트....
2.8s 초기 상황보다는 개선된것을 볼 수 있었다. 움...그래도 조금 더 개선할 수 없을까??
인덱스 설정하기
먼저 Mysql query plan를 통해 쿼리 실행 계획을 살펴보았다.
image 테이블이 테이블 풀 스캔이 되는 것을 볼 수 있었다. 조회 행만 197472....
자세한 필드별 의미를 참고바랍니다.
https://huisam.tistory.com/entry/mysql-plan-query
곰곰히 생각해보니, 이미지 테이블을 조회할 때 항상 no, realwork_code 필드를 조건을 사용했고 카디널리티가 높은 컬럼이였다. 그럼 인덱스로 잡으면 되지 않을까?
no, realwork_code순으로 인덱스 생성 후 테스트를 해보았다.
explain 결과를 보게 됬을 때 인덱스를 타는 것을 볼 수 있고, 0.9s 약 65% 쿼리 성능이 개선된 것을 볼 수 있다.
불필요한 인덱스의 설정은 오히려 B-Tree 용량만 차지하고 insert, delete 간 B-Tree 재구성으로 인해 성능이 떨어질 수 있지만, 조회에 자주 사용되고 카디널리티가 높은 컬럼을 인덱스로 설정한다면 조회 간 성능이 높아지게 된다.
마치며
데이터베이스 전공 수업 간 배운 인덱스의 중요성을 다시 리마인드 할 수 있었다.
다음에는 인덱스를 활용한 no-offset 를 사용하여 실제 성능 측정을 통해 얼만큼 향상되는지 테스트 해봐야겠다.
참고
'DATABASE' 카테고리의 다른 글
페이징 쿼리 개선-커버링 인덱스 (0) | 2024.01.17 |
---|