Query Performance Comparison
쿼리 성능 비교
상황 및 문제
로그인 상태에서 전체 피드 컬렉션을 조회를 해야 한다.
각 컬렉션의 무드를 함께 가져와야 한다.
각 컬렉션의 무드는 여러개 있을 수 있는 상황이다.
로그인 된 유저가 이 컬렉션을 좋아요 눌음 여부도 함께 가져와야 한다.
컬렉션은 페이지 적용한다.
해결방법
방법1: 전체 피드 컬렉션을 모두 조회 후 각 컬렉션의 무드를 가져온다.
예시
JPA의 Subselect 사용
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Immutable
@Subselect("select DISTINCT _feedCollection.id as id " +
", _feedCollection.author_id as author_id " +
", _member.nickname as author_nickname " +
", _taste_mood.name as author_mood " +
", _image.url as author_thumbnail_url " +
", _feedCollection.title as title " +
", _feedCollection.description as description " +
", _like_count.count as like_count " +
", _feedCollection.follower_count as follower_count " +
", _feedCollection.thumbnail_url as thumbnail_url " +
", _feedCollection.is_private as is_private " +
", (select count(id) from feed_collection_feed_ids where feed_collection_feed_ids.id = _feedCollection.id ) as feed_count " +
", (select count(id) from feed_collection_comment_ids where _feedCollectionCommentIds.comment_id = _feedCollection.id) as comment_count " +
", false as liked " +
", _feedCollection.moods_id as moods_id " +
", _feedCollection.created_at as created_at " +
", _feedCollection.updated_at as updated_at " +
"FROM feed_collection _feedCollection " +
"JOIN member _member on _feedCollection.author_id = _member.id " +
"JOIN image _image on _member.profile_image_id = _image.id " +
"JOIN taste_mood _taste_mood on _member.taste_mood_id = _taste_mood.id " +
"LEFT JOIN feed_collection_like_count _like_count on _feedCollection.id = _like_count.feed_collection_id " +
"LEFT JOIN feed_collection_feed_ids as _feedCollecitonIds on _feedCollection.id = _feedCollecitonIds.feed_id " +
"LEFT JOIN feed_collection_comment_ids as _feedCollectionCommentIds on _feedCollection.id = _feedCollectionCommentIds.comment_id "
)
@Table(name = "feed_collection")
public class FeedCollectionSample {
@Id
private FeedCollectionId id;
@AttributeOverride(name = "value", column = @Column(name = "author_id"))
private MemberId authorId;
private String authorNickname;
private String authorMood;
private String authorThumbnailUrl;
private String title;
private String description;
private int likeCount;
private int followerCount;
private String thumbnailUrl;
private boolean isPrivate;
private int feedCount;
private int commentCount;
@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "moods_id")
private FeedCollectionMoods moods;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}결과
page.size() 만큼 Query가 더 발생한다
방법2: 하나의 쿼리로 모든 데이터를 가져온 다음 비지니스 로직에서 grouping하고 translate한다.
예시
QueryDSL
결과
의문?
무조건 하나의 쿼리로 모든 값을 가져오는 것 이 더 빠르고 좋은 방법일까?
테스트
조건
컬렉션 1,000,000건
컬렉션 무드 1,000,000건
이미지 1,000,000건
멤버 1,000,000건
피드 1,000,000건
피드 무드 1,000,000건
피드 좋아요 카운트 1,000,000건
문제 1
조회시 시간이 많이 걸린다
Explain으로 분석


DB 로그 확인 한 실제 쿼리
해결방법
index 추가를 하여 조회 성능 향상
index 추가 검토
FeedCollection 정렬 Query
index 확인
결과!!

FeedCollection의
created_at컬럼에 index 존재 하지 않음
해결
feed_collection테이블:created_at컬럼에 index 추가
Member 테이블 조인
index 확인
결과

Member.id가 Primary Key이므로 index 존재한다.
Image 테이블 조인
index 확인
결과

Image.id도 Primary Key이므로 index 존재한다.
TasteMood 테이블 조인
index 확인
결과

TasteMood.id도 Primary Key이므로 index 존재한다.
FeedCollectionLikeCount 테이블 조인
index 확인
결과 !!

feed_collection_like_count 테이블의 feed_collection_id 컬럼에 index가 존재하지 않음
해결
feed_collection_id 컬럼에 index 추가
FeedCollectionMoods 테이블 조인
index 확인
결과

FeedCollectionMoods.id 도 primary key이므로 index가 존재한다
FeedCollectionMoodsMoodList 테이블 조인
index 확인
결과

FeedCollectionMoodsMoodList.mood_list_id와 feeed_collection_moods_id에 대한 index가 존재한다
FeedCollectionMood 테이블 조인
index 확인
결과

FeedCollectionMood.id 가 primary key이므로 index가 존재한다
FeedCollectionFeedIds 테이블 서브 쿼리
index 확인
결과

feed_collection_id가 index가 존재한다
FeedCollectionCommentIds 테이블 서버 쿼리
index 확인
결과

comment_id가 index가 존재한다
FeedCollectionLike 테이블 서버 쿼리
index 확인
결과 !!

feed_collection_like 테이블의 member_id,feed_collection_id 컬럼에 index 가 없음 해결 feed_collection_like 테이블: member_id,feed_collection_id 컬럼에 index 추가
해결


더미 데이터에서는 feed_collection_id의 cardinality 가 더 높아서 feed_collection_id를 first Index로 하는 것이 유리 하다
index 추가 후(Explain)

MySQl Workbench Visual Explain오류가 있음

문제 2
기존 Subselect 조회가 안됨(시간이 너무 오래 걸림...)
방법1: 전체 피드 컬렉션을 모두 조회 후 각 컬렉션의 무드를 가져온다.
분석


페이징이 풀스캔 조회 후 적용되기 때문에 속도가 매우 늦음
해결방법
개선한 Query
subSelect를 사용하지 않고 PrimarySelect를 사용해서 데이터를 가져온 다음 feedcollectionId로 Mood를 가져온다


Page.size() 만큼 무드 가져온다
성능 비교
방법1: 전체 피드 컬렉션을 모두 조회 후 각 컬렉션의 무드를 가져온다.
실행시간
1.1649849170.9728298750.4282660.293842250.2906873330.703994250.4946789590.2663298750.2811430.662992458
방법2: 하나의 쿼리로 모든 데이터를 가져온 다음 비지니스 로직에서 grouping하고 translate한다.
실행시간
1.0396708751.1190289171.126122750.3561430410.3392148750.2132849580.4225051670.4356428750.2470296250.1757515
결론
대량 데이터를 조회시 index 설계는 반드시 고려해야 할 사항이다
함부로 subselect를 사용하면 안된다
단일 쿼리로 모든 데이터를 가져오는 것과 제한된 여러 쿼리로 데이터를 나눠서 가져오는 것 사이에는 속도 면에서 큰 차이는 없다
Last updated
Was this helpful?