코딩항해기
[DBMS] N+1 문제 본문
N+1 문제
N+1 문제는 ORM을 사용할 때 발생하는 대표적인 성능 문제다. 하나의 쿼리로 N개의 데이터를 가져온 후, 연관된 데이터를 가져오기 위해 N번의 추가 쿼리가 발생하는 현상을 말한다.
예시
게시글과 댓글의 관계를 예로 들면, 한 게시글에는 여러 개의 댓글이 달릴 수 있는 일대다 관계일 때,
더보기
@Entity
public class Post {
@Id
@GeneratedValue
private Long id;
private String title;
@OneToMany(mappedBy = "post", fetch = FetchType.LAZY)
private List<Comment> comments;
}
@Entity
public class Comment {
@Id
@GeneratedValue
private Long id;
private String content;
@ManyToOne(fetch = FetchType.LAZY)
private Post post;
}
// 최초 게시글 목록을 가져오는 쿼리 1번 실행
List<Post> posts = postRepository.findAll();
// 각 게시글의 댓글을 조회할 때마다 추가 쿼리 발생
for (Post post : posts) {
List<Comment> comments = post.getComments(); // N번의 추가 쿼리 실행
System.out.println("댓글 수: " + comments.size());
}
N+1문제가 발생한다.
해결하기
이를 해결하기 위해서는 fetch join을 사용하거나, EntityGraph를 사용하는 방법이 있다.
Fetch join의 경우 연관된 엔티티를 join문으로 한 번에 조회하는 방법이고, EntityGraph의 경우는 JPA가 제공하는 어노테이션을 활용하는 방법이다. 또는 BatchSize를 지정해 연관된 데이터를 조회할 때 지정된 크기만큼 한 번에 조회하는 방법을 사용할 수 있다.
Fetch 방식
@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
@Query("SELECT p FROM Post p JOIN FETCH p.comments")
List<Post> findAllWithComments();
}
// 사용 예시
List<Post> posts = postRepository.findAllWithComments(); // 한 번의 쿼리로 모든 데이터 조회
Fetch 방식 쿼리 비교
SELECT * FROM post; -- 1번 쿼리
SELECT * FROM comment WHERE post_id = 1; -- N번 실행
SELECT * FROM comment WHERE post_id = 2;
SELECT * FROM comment WHERE post_id = 3;
...
⇃
SELECT p.*, c.*
FROM post p
LEFT JOIN comment c ON p.id = c.post_id; --쿼리 하나로 해결
N+1 문제를 해결할 때는 상황에 맞는 적절한 전략을 선택해야 한다. 데이터의 양이 많은 경우 Fetch Join은 메모리 사용량이 증가할 수 있으며, 페이징 처리가 불가능할 수 있다. 이런 경우 BatchSize 설정이나 다른 최적화 방법을 고려해야 한다.
'DBMS' 카테고리의 다른 글
[DBMS] ACID (0) | 2024.12.19 |
---|---|
[DBMS] Eager Loading & Lazy Loading (0) | 2024.12.18 |
[DBMS] ORM (Object-Relational Mapping) (0) | 2024.12.17 |
[DBMS] PostgreSQL 이해 (ORDBMS) (0) | 2024.11.23 |
[DBMS] MongoDB 이해 (NoSQL) (0) | 2024.11.22 |