본문 바로가기
Spring Framework

[Spring Framework] N + 1 Query 문제, default_batch_fetch_size로 해결

by 올리브영 2023. 3. 30.
728x90
반응형

Question엔티티 구성은 아래와 같다.

@Getter
@Setter
@Entity
public class Question {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(length = 200)
    private String subject;

    @Column(columnDefinition = "TEXT")
    private String content;

    private LocalDateTime createDate;

    @OneToMany(mappedBy = "question", cascade = CascadeType.REMOVE)
    private List<Answer> answerList;
}

 

Question엔티티를 게시판 형태로 나온다.

게시판을 불러오기 위해서 N+1 문제가 발생된다.

 

아래 쿼리를 통해 살펴보자.

 

게시물 조회 Query

// 게시물을 불러오는 쿼리
SELECT q1_0.id,
q1_0.content,
q1_0.create_date,
q1_0.subject 
FROM question q1_0 
ORDER BY q1_0.create_date DESC LIMIT 290, 10;

페이징을 이용하기 위한 쿼리

// 페이징을 이용하기 위한 쿼리
SELECT COUNT(q1_0.id) 
FROM question q1_0;

답변 개수 조회 쿼리

SELECT a1_0.question_id,
a1_0.id,
a1_0.content,
a1_0.create_date 
FROM answer a1_0 
WHERE a1_0.question_id = 10;

SELECT a1_0.question_id,
a1_0.id,
a1_0.content,
a1_0.create_date 
FROM answer a1_0 
WHERE a1_0.question_id = 9;

SELECT a1_0.question_id,
a1_0.id,
a1_0.content,
a1_0.create_date 
FROM answer a1_0 
WHERE a1_0.question_id = 8;

SELECT a1_0.question_id,
a1_0.id,
a1_0.content,
a1_0.create_date 
FROM answer a1_0 
WHERE a1_0.question_id = 7;

SELECT a1_0.question_id,
a1_0.id,
a1_0.content,
a1_0.create_date 
FROM answer a1_0 
WHERE a1_0.question_id = 6;

SELECT a1_0.question_id,
a1_0.id,
a1_0.content,
a1_0.create_date 
FROM answer a1_0 
WHERE a1_0.question_id = 5;

SELECT a1_0.question_id,
a1_0.id,
a1_0.content,
a1_0.create_date 
FROM answer a1_0 
WHERE a1_0.question_id = 4;

SELECT a1_0.question_id,
a1_0.id,
a1_0.content,
a1_0.create_date 
FROM answer a1_0 
WHERE a1_0.question_id = 3;

SELECT a1_0.question_id,
a1_0.id,
a1_0.content,
a1_0.create_date 
FROM answer a1_0 
WHERE a1_0.question_id = 2;

SELECT a1_0.question_id,
a1_0.id,
a1_0.content,
a1_0.create_date 
FROM answer a1_0 
WHERE a1_0.question_id = 1;

해결방안

답변 개수를 조회하는 쿼리가 10개 실행된것을 볼 수 있는 이는 비효율적인것을 볼 수 있다.

만약 한 페이지에 질문 게시글이 100개씩 보여준다고 하면 100개의 쿼리가 실행된다는 것이다.

이러한 문제를 해결하기 위해서 default_batch_fetch_size를 설정해줘야한다.

 

BatchSize 설정

spring.jpa.properties.hibernate.default_batch_fetch_size=1000 // 1000개로 설정

 

default_batch_fetch_size는 복잡한 조회 쿼리를 IN절을 이용해서 한꺼번에 처리할 수 있다. 성능 개선에 유용하다.

 

default_batch_fetch_size를 이용한 답변 개수 조회 쿼리

SELECT a1_0.question_id,
a1_0.id,
a1_0.content,
a1_0.create_date 
FROM answer a1_0 
WHERE a1_0.question_id IN (10, 9, 8, 7, 6, 5, 4, 3, 2, 1);

결과는 똑같지만 쿼리 개수가 줄어들어서 더 효율적이다. 이는 데이터가 많은 경우에 유리하다.

 

 

만약 배치사이즈를 5로 설정하게 되면 아래와 같이 5 5 나눠서 2개의 쿼리가 실행된다.

SELECT a1_0.question_id,
a1_0.id,
a1_0.content,
a1_0.create_date 
FROM answer a1_0 
WHERE a1_0.question_id IN (10, 9, 8, 7, 6);

SELECT a1_0.question_id,
a1_0.id,
a1_0.content,
a1_0.create_date 
FROM answer a1_0 
WHERE a1_0.question_id IN (5, 4, 3, 2, 1);

하지만 여기서 드는 의문은 사이즈를 엄청 큰 숫자로 해서 하면 좋을텐데 왜 배치사이즈를 1000개로 한정을 했는지 라는 생각을 하게 된다.

 

객체 1000개가 메모리에 올라가는 거니까 램을 사용한다는건데 배치사이즈를 1조개로 설정하면 1조개의 객체가 메모리에 한꺼번에 올라가면 느려지거나 작동이 안될수 있기때문이다.

 

그래서 보통 배치사이즈는 1000개로 설정한다.

728x90
반응형