Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: 게시글 목록 조회 성능 최적화(#76) #81

Merged
merged 9 commits into from
Nov 28, 2024

Conversation

yooooonshine
Copy link
Member

@yooooonshine yooooonshine commented Nov 28, 2024

issue

현재 문제

Post 목록 조회시 페이지네이션으로 10개를 조회해온다.
Post 목록 조회에서 병목 현상이 발생했다.

원인분석

조회 한번당 쿼리가 생각보다 많이나간다.

  • member 조회 (인증)
  • post 목록 조회(1번)
    • vote 조회(10번)
    • like 조회(10번)
    • views 조회(10번)
    • parent comment 조회(10번)
    • child comment 조회(10번)
      도합 52번의 쿼리문이 나간다.

부하테스트 상황(서버)

1번 요청 -> 0.395초
500번 요청 -> 평균 5.260초
1000번 요청 -> 평균 12.797초

에러는 발생하지 않았다.

해결

방법1. 좋아요, 조회수, 투표 수 등의 연관관계를 모두 끊고, 숫자만 갖고 있는다.

PostView 엔티티 Redis 로 변경

기존의 PostView

@Entity
@Getter
@Table(
	name = "post_views",
	uniqueConstraints = {
		@UniqueConstraint(columnNames = {"post_id", "member_id"})
	})
@NoArgsConstructor(access = PROTECTED)
public class PostView extends BaseEntity {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "view_id")
	private Long viewId;

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "post_id")
	private Post post;

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "member_id")
	private Member member;

	@Builder
	public PostView(Post post, Member member) {
		this.post = post;
		this.member = member;
	}
}

변경된 PostView

@Getter
@NoArgsConstructor(access = PROTECTED)
@RedisHash(value = "postView" , timeToLive = 1209600)
public class PostView {

	@Id
	private String id;

	private Long postId;
	private Long memberId;

	@Builder
	private PostView(Long postId, Long memberId) {
		this.id = generateId(postId, memberId);
		this.postId = postId;
		this.memberId = memberId;
	}

	public static String generateId(Long postId, Long memberId) {
		return postId + ":" + memberId; // 고유 Key 생성
	}
}

기존의 Post의 PostView연관관계

	@OneToMany(mappedBy = "post", fetch = FetchType.LAZY, orphanRemoval = true, cascade = {CascadeType.REMOVE})
	private List<PostView> postViews = new ArrayList<>();

변경된 Post의 PostView(연관관계를 끊고 조회수만 저장한다.)

	@Column(columnDefinition = "INT DEFAULT 0")
	private Integer postViewsCount = 0;

PostLike 연관관계 제거

기존의 Post의 PostLikesCount

	@OneToMany(mappedBy = "post", fetch = FetchType.LAZY, orphanRemoval = true, cascade = {CascadeType.REMOVE})
	private List<PostLike> postLikes = new ArrayList<>();

변경된 PostLikeCount

	@Column(columnDefinition = "INT DEFAULT 0")
	private Integer postLikesCount = 0;

BatchSize를 통한 최적화

게시글 목록 조회는 페이지네이션으로 인해 한번에 10개씩 조회해온다.

Post는 {vote, parentComment, childComment}의 연관관계를 갖고 있다.

Post 목록 10개를 조회할 때 BatchSize를 적용하면

  • Post 목록 조회 (1번)
  • postId 10개에 따른 Vote 목록 조회(1번)
  • postId 10개에 따른 parentComment 목록 조회(1번)
  • postId 10개에 따른 childComment 목록 조회(1번)

총 4회의 쿼리를 통해 데이터를 가져올 수 있다.

적용

image
연관 엔티티에 별도로 @batchsize 애노테이션을 붙여줬다.

결과

DB 캐싱 전 1번 요청 : 0.620초-> 0.352초
캐싱 후 1번 요청 : 0.281초 -> 0.183초
캐싱 후 500번 요청 : 평균 9.260초 -> 5.74초
캐싱 후 1000번 요청 : 평균 162797초 -> 7.74초

대략 40~50% 정도 성능이 개선됐다.

@yooooonshine yooooonshine linked an issue Nov 28, 2024 that may be closed by this pull request
1 task
@yooooonshine yooooonshine merged commit 9631122 into develop Nov 28, 2024
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Refactor: N + 1 문제 해결
1 participant