성능 최적화(2) - 읽기 전용

2024. 2. 13. 21:17spring/JPA

엔티티가 영속성 컨텍스트에 관리되면 1차 캐시부터 변경 감지까지 얻을 수 있는 혜택이 많다. 하지만 영속성 컨텍스트는 변감 감지를 위해 스냅샷 인스턴스를 보관하므로 더 많은 메모리를 사용하는 단점이 있다.만약 조회한 데이터를 수정할 일이 없고 같은 트랜잭션 안에서 다시 읽어들일 일도 없다면 읽기 전용으로 엔티티를 조회해서 메모리 사용량을 최적화 할 수 있다.

 

select o from Order o

 

 

1. 스칼라 타입으로 조회

가장 황실한 방법은 다음처럼 엔티티가 아닌 스칼라 타입으로 모든 필드를 조회하는 것이다. 엔티티가 아니므로 영속성 컨텍스트가 결과를 관리하지 않는다.

select o.id, o.name, o.price from Order o

 

 

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

하이버네이트 전용 힌트인 org.hibernate.readOnly를 사용하면 엔티티를 읽기 전용이므로 영속성 컨텍스트는 스냅샷을 보관하지 않는다.

TypeQuery<Order> query = em.createQuery("SELECT o FROM Order o", Order.class);
query.setHint("org.hibernate.readOnly", true);

 

 

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

스프링 프레임워크를 사용하면 트랜잭션을 읽기 전용 모드로 설정할 수 있다.

@Transactional(readOnly = true)

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

 

읽기 전용 데이터를 조회할 때, 메모리를 최적화하려면 스칼라 타입으로 조회하거나 하이버네이트가 제공하는 읽기 전용 쿼리 힌트를 사용하면 되고 플러시 호출을 막아서 속도를 최적화하려면 읽기 전용 트랜잭션을 사용하면 된다. 따라서 읽기 전용 트랜잭션과 읽기 전용 쿼리 힌트를 동시에 사용하는 것이 가장 효과적이다.

 

@Tranctional(readOnly = true) //읽기 전용 트랜잭션
public List<Member> findAll() {
    return em.createQuery("SELECT m FROM Member m", Member.class)
    		.setHint("org.hibernate.readOnlyy", true) //읽기 전용 쿼리 힌트
            .getResultList();
}