데이터 증폭이란?
팀과 멤버가 1 : N인 관계에서 DB를 조회할 때 이미지처럼 Team이 여러 번 함께 조회되는 건 어쩔 수 없다.
하지만 이미지처럼 전체적인 데이터가 필요한 게 아니라, Team Name만 필요한 상황이라면 어떨까?
fetch join을 사용하는 쿼리에서 Team Name을 조회하면 JPA는 자동으로 두 엔티티를 조인한 뒤에 Team Name을 조회하기 때문에 동일한 값도 여러 번 조회되는 문제가 발생한다.
이게 바로 데이터 증폭이다.
데이터 증폭이 발생하는 상황에서는 distinct 사용으로 동일한 값을 여러 번 출력하지 않도록 한다.
fetch join을 사용하는 이유
엔티티가 서로 연관 관계를 가질 때, 불필요한 상황에서 관련된 엔티티의 데이터를 모두 조회하면 성능이 좋지 않기 때문에 대부분의 상황에서 지연 로딩을 사용하게 된다.
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "requester_id", nullable = false)
private User requester;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "receiver_id", nullable = false)
private User receiver;
지연 로딩을 사용하는 엔티티가 큰 규모의 반복문 처리가 필요한 상황이 있다고 가정하자.
그럼 가져온 엔티티를 재사용하는 게 아니라, 지연 로딩으로 인해 반복문을 돌 때마다 DB에 접근하여 데이터를 조회하게 된다.
이 문제를 N+1 문제라고 한다.
처음 DB 조회한 횟수 1과 반복문을 돌 때마다 N번 DB 조회를 실행해서 N+1이다.
N+1 문제를 해결하기 위해 fetch join을 사용해서 연관된 엔티티의 데이터까지 한 번에 조회하는 방법이 사용된다.
하지만 fetch join의 문제는 처음에 말했다시피 데이터 증폭이 발생할 수 있고, 그로 인해서 페이징도 제대로 적용되지 않을 가능성이 크다.
fetch join 대신 배치 사이즈
사실상 배치 사이즈(@BatchSize)를 사용해서 N+1 문제를 해결하는 게 가장 간단한 방법이다.
@ManyToOne(fetch = FetchType.LAZY)
@BatchSize(size = 10) // 한 번에 10개씩 로딩
private Team team;
// 또는 application.properties에서 hibernate.jdbc.batch_size=10으로 설정
이렇게 10으로 값을 설정하면, 해당 데이터에 대해 10번의 DB 접근 요청이 발생해야 DB에 접근하여 쿼리를 실행한다.
배치 사이즈를 사용하면 데이터 증폭이 발생하지 않기 때문에 페이징도 적용이 가능하다.
해당 쿼리가 어느정도 발생해야 DB 요청을 실행하기 때문에 성능 최적화에 적절한 방법이다.
그렇다면 어떤 상황에서 fetch join 또는 배치 사이즈를 사용해야 할까?
데이터를 조회하는 과정에서 연관된 데이터가 많다면 배치 사이즈를 사용
적은 양의 데이터를 빠르게 조회해서 처리해야 하는 상황에서는 fetch join을 사용
탈 JPA?
사실상 데이터 증폭, N+1 문제는 JPA(연관관계 설정, 지연 로딩)에 의해 발생하는 문제다.
이 문제를 방지하기 위해 아예 연관 관계를 두지 않는 DB 설계도 있다.
댓글과 게시글처럼 연관성이 짙은 경우를 제외하면 굳이 서로 연관 관계를 설정해서 동일한 생명주기를 가질 필요가 없기 때문이다.
(이런 상황에서는 nativeQuery를 사용해서 DB에서 데이터를 조회한다.)
대기업처럼 큰 데이터를 관리하는 상황에서는 이렇게 연관성이 큰 데이터를 다룰 일이 많지 않기 때문에 nativeQuery를 사용하는 경우가 대부분이고 JPA를 잘 사용하지 않지만, 지금의 내가 하는 프로젝트나 과제는 규모가 작으면서 각 엔티티가 서로 연관성을 가지기 때문에 JPA를 사용하는 게 적절하다.
'언어, 프레임워크 > Spring' 카테고리의 다른 글
JPA 테이블 전략 (2) | 2025.04.17 |
---|---|
JPA 연관관계 (2) | 2025.04.16 |
Converter, Formatter (0) | 2025.04.15 |
영속성 컨텍스트에서의 insert 시점 실습 (0) | 2025.04.06 |
JPA 알아보기 (+Spring Data JPA 적용) (0) | 2025.03.31 |