본문 바로가기

JPA

[JPA] 페치조인(fetch join)

이번 포스팅에서는 JPQL을 사용할때, 성능최적화를 위해 사용되는 페치조인(fetch join)에 대해서 알아보려고합니다.

먼저, 페치조인은 어떤 성능문제가 발생할때 사용되는 것일까요??

바로 JPA fetch전략인 지연로딩(Lazy Loading)과 즉시로딩(Eager Loading)에 의해 N+1문제가 발생할때, 주로 사용하게 됩니다. 그럼 N+1문제가 발생하는 예제코드를 보도록 하겠습니다.

 

예제코드(N+1문제가 발생하는 코드)
@Entity
public class Member{
    @Id
    @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USERNAME")
    private String username;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "TEAM_ID")
    private Team team;
    // getter,setter 생략..
}
@Entity
public class Team extends BaseEntity{

    @Id
    @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;
    private String name;

    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();
    // getter,setter 생략...
}

Member와 Team은 페치타입이 지연로딩으로 설정이 되어있고, Member와 Team 모두 persist한뒤 EntityManager를 깨끗하게 초기화 시켜두었습니다. 이렇게 한 이유는 1차캐시를 완벽하게 없애기 위해서입니다.

이제부터는 아래 서로다른 2개의 코드에서 N+1문제가 언제 발생하는지 알아보도록 하겠습니다.

[N+1이 발생하지 않는경우]

  1. .getResultList()를 하면서 지연로딩이므로 Member테이블에서만 데이터를 조회한다.
  2. Member데이터를 1차캐시에 저장한다.
  3. 반복문을 돌면서 member.getUsername()을 가져오지만, 1차캐시에 Member정보가 저장되어 있어서 추가적인 쿼리가 발생하지 않는다.

[N+1이 발생하는 경우]

  1. .getResultList()를 하면서 지연로딩이므로 Member테이블에서만 데이터를 조회한다.
  2. Member데이터를 1차캐시에 저장한다.
  3. 반복문을 돌면서 member.getTeam.getName을 가져올때, team정보는 1차캐시에 없기때문에 프록시로 만들어진 team데이터를 조회하기위해 select쿼리를 실행한다.
  4. 회원1, 회원2는 팀이름이 korea1로 동일하기때문에 회원1의 팀이름인 korea1이 1차캐시에 등록되게 되면 회원2의 팀이름을 조회할때, 1차캐시에 등록되어있는 정보를 사용하면 되므로 select쿼리가 나가지 않는다.
  5. 1번째, 2번째 회원정보가 System.out.println()으로 출력되면 마지막 3번째 회원정보를 출력하려고하는데, 이때 다시 select쿼리가 나가게된다. 그 이유는 회원1,2의 팀은 korea1이지만, 회원3의 팀은 korea2이다. korea2의 팀정보는 1차캐시에 등록되어 있지 않기때문에 select쿼리가 실행되게 된다.

이런 경우처럼 모든 회원의 팀이 모두 다를경우!!!

N+1문제가 발생하여 성능상의 이슈가 나타날 수 있습니다.

 

예제코드(fetch조인을 통해 N+1문제 해결)

참고로 JPA에서 사용하는 fetch조인은 SQL조인방식이 아닙니다. 단순 성능최적화를 위한 기능입니다.

  1. 기존 쿼리에서 join fetch m.team을 추가하여 페치조인을 실행한다.
  2. 페치조인을 실행하면 즉시로딩처럼 연관된 테이블의 정보를 한번에 가져온다.
  3. Member와 Team의 모든 데이터가 1차캐시에 저장되어 있기때문에 추가적인 select쿼리가 나가지 않게되어 N+1문제를 해결하게 된다.

이렇게 페치조인을 사용하게 되면, N+1문제로인한 성능문제를 해결할 수 있게됩니다.

'JPA' 카테고리의 다른 글

[JPA] 컬렉션 조회 최적화  (0) 2022.02.12