흰 스타렉스에서 내가 내리지

읽기 전용 쿼리의 성능 최적화 - @Transactional(readonly=true), setHint(readonly) 본문

JPA

읽기 전용 쿼리의 성능 최적화 - @Transactional(readonly=true), setHint(readonly)

주씨. 2024. 4. 28. 03:48
728x90

# 엔티티가 영속성 컨텍스트에 관리되면...

  • 장점 : 1차캐시, 변경 감지
  • 하지만 스냅샷 인스턴스를 보관하므로 더 많은 메모리를 사용한다. 

 

 

읽기 전용으로 엔티티를 조회하면 메모리 사용량을 최적화 할 수 있다.

 

# 스칼라 타입으로 조회

select o.id, o.name from Order o
  • 스칼라 타입은 영속성 컨텍스트가 결과를 관리하지 않는다.

 

# 읽기전용 쿼리 힌트 사용

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

 

# 읽기 전용 트랜잭션 사용

@Transactional(readOnly = true)
  • 트랜잭션에 readOnly = true 옵션을 주면 스프링 프레임워크가 하이버네이트 세션의 플러시 모드를 MANUAL 로 설정한다.
  • 이렇게 하면 강제로 플러시를 호출하지 않는 한 플러시가 일어나지 않는다.
  • 따라서 트랜잭션을 커밋해도 영속성 컨텍스트를 플러시하지 않는다.
  • 영속성 컨텍스트를 플러시하지 않으니 엔티티의 등록, 수정, 삭제는 당연히 동작하지 않는다.
  • 하지만 플러시할 때 일어나는 스냅샷 비교와 같은 무거운 로직들을 수행하지 않으므로 성능이 향상된다. 
  • 물론 트랜잭션을 시작했으므로 트랜잭션 시작, 로직수행, 트랜잭션 커밋의 과정은 이루어진다. 단지 영속성 컨텍스트를 플러시하지 않을 뿐이다. 

 

# 결론

1. 메모리 최적화

  • 스칼라 타입으로 조회하거나 하이버네이트가 제공하는 읽기 전용 쿼리힌트를 사용

2. 속도 최적화

  • 읽기 전용 트랜잭션을 사용하여 플러시 호출을 막음
@Transactional(readOnly = true)		//-- 1. 읽기 전용 트랜잭션
public List<DataEntity> findDatas(){

    return em.createQuery("select d from DataEntity d",
        DataEntity.class)
        .setHint("org.hibernate.readOnly", true)    //-- 2. 읽기 전용 쿼리 힌트
        .getResultList();
}
  1. 읽기 전용 트랜잭션 : 플러시를 작동하지 않도록 해서 성능 향상
  2. 읽기 전용 엔티티 사용 : 엔티티를 읽기 전용으로 조회해서 메모리 절약