게시글 리스트를 출력할 때 N+1 문제 발생

‍bng4535·2023년 4월 3일
0

문제 상황

  • 게시판의 1페이지의 크기가 10일 때 질문 중 10개의 질문을 paging을 통해 출력한다.
// question 테이블이 생김
@Getter
@Setter
@Entity
@ToString
public class Question {
    @Id // PRIMARY KEY
    @GeneratedValue(strategy = GenerationType.IDENTITY) // AUTO_INCREMENT
    private Integer id; // INT id

    @Column(length = 200) // VARCHAR(200)
    private String subject;

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

    private LocalDateTime createDate; // DATETIME

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

    public void addAnswer(Answer a) {
        a.setQuestion(this);
        answerList.add(a);
    }
}
...
 <td>
 	<a th:text="${q.subject}" th:href="@{|/question/detail/${q.id}|}"></a>
    <span class="text-danger small ms-2"
    	th:if="${#lists.size(q.answerList) > 0}"
        th:text="${#lists.size(q.answerList)}">
    </span>
 </td>
...

문제

  • 기본적으로 spring에서는 객체의 연관관계를 지연 로딩(Lazy Loading)으로 처리하기 때문에, 처음에 답변 리스트를 불러오는 쿼리를 수행한 다음, 답변에 관한 데이터처리가 요청될 때 객체 내용을 가져오게 된다.
  • 위 코드에서는 list.size(q.answerList)에 의해 추가적인 쿼리가 발생하여 N+1문제가 생긴다.

해결

1. batch_fetch_size 늘리기

  • 지연 로딩으로 설정된 엔티티들을 일괄적으로 한 번의 쿼리로 가져온다.
  • 한 번의 쿼리로 여러 개의 엔티티를 조회한 다음에, 가져오는 엔티티들의 ID를 기반으로 추가 쿼리를 여러 번 수행하여 데이터를 가져오는 것
  • 지연 로딩을 유지하면서 성능을 개선
  • 한번에 많은 데이터를 가져오므로 메모리 문제와 추가적인 쿼리 실행으로 인한 성능 저하 문제 발생

2. fetch join

  • 하나의 쿼리로 여러 엔티티와 연관된 데이터를 가져오는 방식
  • 연관된 엔티티를 지연 로딩 없이 즉시 로딩
  • 필요하지 않은 데이터와 중복데이터의 발생으로 인한 메모리 문제

비교

  • batch_fetch
    대량의 데이터를 가져와야 할 때, 즉 엔티티와 연관된 데이터가 많은 경우 사용하기 적합
  • fetch join
    여러 엔티티를 조인해서 사용해야 할 때 적합
profile
공부 기록

0개의 댓글