개발
home
🏑

[JPA] 읽기 전용 쿼리 성능 최적화. @Transactional(readOnly = true) 알아보기

Created
2023/01/25
Tags
JPA
읽기 전용 쿼리
성능 최적화
2023-01-25 @이영훈
엔티티(Entity)를 영속성 컨텍스트에 관리하면 1차 캐시와 변경 감지(Dirty Check) 등의 이점을 얻을 수 있습니다
하지만 조회만을 위해서 엔티티를 영속성 컨텍스트에 저장하면, 변경 감지를 위해 스냇샷 인스턴스를 보관해서 메모리 사용량이 늘어나는 단점이 있습니다
그리고 플러시(flush)가 일어나서 플러시 때 발생하는 스냅샷 비교와 같이 무거운 로직이 수행됩니다
그래서 읽기 전용 쿼리로 성능 최적화를 할 수 있습니다
최적화하는 방식은 다음 4가지 방식이 있습니다
처음 두 가지 방식은 영속성 컨텍스트와 관련된 방식이고, 나머지 두 가지 방식은 플러시와 관련된 최적화 방식입니다

1. 스칼라 타입으로 조회

스칼라 타입으로 조회하면, 영속성 컨텍스트가 결과를 관리하지 않습니다
select o.id, o.name, o.price from Order o
SQL
복사

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

하이버네이트의 전용 힌트 org.hibernate.readOnly 를 사용하면 엔티티를 읽기 전용으로 조회할 수 있습니다
하이버네이트 전용 힌트를 사용하면, 영속성 컨텍스트는 스냇샷을 보관하지 않습니다
TypedQuery<Order> query = em.createQuery("select o from Order o", Order.class); query.setHint("org.hibernate.readOnly", true);
Java
복사

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

스프링 프레임워크를 사용하면 트랜잭션을 읽기 전용 모드로 설정할 수 있습니다
import org.springframework.transaction.annotation.Transactional @Transactional(readOnly = true)
Java
복사
readOnly = true 옵션을 주면 스프링 프레임워크가 하이버네이트 세션의 플러시 모드를 MANUAL로 설정합니다
MANUAL 모드에서는 강제로 플러시를 호출하지 않으면 플러시가 일어나지 않습니다
따라서 트랜잭션을 커밋해도 영속성 컨텍스트를 플러시하지 않습니다
(트랜잭션을 시작했으므로 트랜잭션 시작, 로직수행, 트랜잭션 커밋의 과정은 이루어집니다)
영속성 컨택스를 플러시 하지 않기 때문에 스냅샷 비교와 같은 무거운 로직들을 수행하지 않으므로 성능이 최적화됩니다

4. 트랜잭션 밖에서 읽기

트랜잭션 밖에서 읽는다는 것은 트랜잭션 없이 엔티티를 조회한다는 뜻입니다
물론, JPA에서 데이터를 변경하려면 트랜잭션은 필수입니다. 조회만 하기 위해서만 사용해야 합니다
스프링 프레임워크를 사용하면 다음처럼 설정합니다
import org.springframework.transaction.annotation.Propagation import org.springframework.transaction.annotation.Transactional @Transactional(propagation = Propagation.NOT_SUPPORTED)
Java
복사
트랜잭션 없이 실행하고, 트랜잭션이 있다면 현재 트랜잭션을 일시 중단합니다
트랜잭션을 사용하지 않으면 플러시가 일어나지 않으므로 조회 성능이 향상됩니다
(트랜잭션을 커밋하면 플러시가 발생하겠지만 트랜잭션이 없기 때문에 커밋할 것이 없습니다. 그래서 애초에 플러시가 발생할 여지가 없는 것입니다)
정리하면
메모리를 최적화
스칼라 타입으로 조회 select o.id, o.name, o.price from Order o
하이버네이트가 제공하는 읽기 전용 힌트 query.setHint("org.hibernate.readOnly", true);
플러시 호출 방지
스프링의 읽기 전용 트랜잭션 @Transactional(readOnly = true)
트랜잭션 밖에서 읽기 @Transactional(propagation = Propagation.NOT_SUPPORTED)

Reference