[JPA] 읽기 전용 쿼리의 성능 최적화

우잼·2024년 1월 27일
post-thumbnail

본 글은 인프런 김영한님의 자바 ORM 표준 JPA 프로그래밍 책을 기반으로 정리한 내용입니다.


1. 들어가기전

엔티티가 영속성 컨텍스트에 관리되면 1차 캐시부터 변경 감지까지 이점이 많다. 하지만 영속성 컨텍스트는 변경 감지를 위해 스냅샷 인스턴스를 메모리에 보관하므로 메모리를 많이 사용한다는 단점이 있다.

아래의 예시 사항을 참고하여 보자.

  • 100건의 구매 내역을 출력하는 로직이 있다고 가정하자.
    -> 그렇다면 조회한 구매 내역을 다시 조회하거나 수정할 일이 없이 딱 한번만 화면에 출력하면 되므로 읽기 전용 으로 엔티티를 조회하면 메모리 사용량을 최적화할 수 있을 것이다.

다음 JPQL 쿼리를 최적화 해보자.

select o from Order o

2. 최적화

2-1. 스칼라 타입으로 조회

  • 가장 확실한 방법으로는 엔티티가 아닌 스칼라 타입으로 모든 필드를 조회하는 것이다. 스칼라 타입은 영속성 컨텍스트가 관리하지 않기 때문이다.
    select o.id, o.name, o.price from Order o

2-2. 읽기 전용 쿼리 힌트 사용

  • hibernate 전용 힌트인 org.hibernate.readOnly를 사용하는 방법이다.
    TypeQuery<Order> query = em.createQuery("select o from Order o", Order.class);
    query.setHint("org.hibernate.readonly", true);
  • 읽기 전용이므로 영속성 컨텍스트는 스냅샷을 보관하지 않으므로 메모리를 최적화할 수 있으나 엔티티를 수정해도 데이터베이스에 반영되지 않는다.

2-3. 읽기 전용 트랜잭션 사용

  • 스프링 프레임워크를 사용하면 트랜잭션을 읽기 전용 모드로 설정할 수 있다.
    @Transactional(readOnly = true)
  • 트랜잭션에 readOnly=true 옵션을 주면 스프링 프레임워크가 하이버네이트 세션의 플러시 모드를 MANUAL로 설정한다.
  • 트랜잭션을 커밋하더라도 강제로 플러시를 호출하지 않는 한 플러시가 발생하지 않는다.
  • 엔티티의 등록, 수정, 삭제는 동작하지 않지만 성능이 향상된다

2-4. 트랜잭션 밖에서 읽기

  • 트랜잭션 없이 엔티티를 조회한다는 의미로 조회 성능이 향상된다.

  • 조회가 목적일 때만 사용해야 한다.

    // spring
    @Transactional(propagation = Propagation.NOT_SUPPORTED) 
    
    // J2EE
     @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED))

3. 정리

  • 읽기 전용 데이터 조회

    - 메모리 최적화

    1. 스칼라 타입으로 조회
    2. 하이버네이트가 제공하는 읽기 전용 쿼리 힌트 사용

    - 속도 최적화

    1. 읽기 전용 트랜잭션 사용
    2. 트랜잭션 밖에서 읽기

    (참고: 스프링 프레임워크의 경우 읽기 전용 트랜잭션을 사용하는 것이 편리)

  • 읽기 전용 트랜잭션과 읽기 전용 쿼리 힌트를 동시에 사용하는 것이 가장 효과적

    Transactional(readOnly = true) (1). 읽기 전용 트랜잭션
    public ... () {
    	return em.createQuery("select ...", ...class)
       	.setHint("org.hibernate.readOnly", true) // (2). 읽기 전용 쿼리 힌트
           .getResultList();
    }
    	(1). 읽기 전용 트랜잭션 사용: 플러시를 작동하지 않아 성능 향상
        (2). 읽기 전용 엔티티 사용: 엔티티를 읽기 전용으로 조회하여 메모리 절약
    
    
profile
나는 재민

0개의 댓글