@Transactional(readOnly = true) 꼭 써야하나요?

나르·2024년 2월 18일
1

Spring

목록 보기
24/25
post-thumbnail

이전에 Transactional 에 대한 글을 정리하면서 readOnly에 대해 간략하게 다뤄보았다.
짧게 얘기하자면 @Transactional(readOnly=true)로 설정할 경우 flushmode가 manual로 설정되고 더티체킹을 위한 영속성 컨텍스트 관리 등을 생략하기 때문에 성능을 개선할 수 있다는 내용이었다.

그런데 그럴거면 그냥 트랜잭션 자체를 잡지 않아서 생성 비용을 줄이는게 낫지 않나?
Master-Slave 분기도 사실 따로 세팅해야 적용되니 @Transactional(readOnly=true) 자체는 크게 하는게 없지 않나? 라는 생각에서 아래 내용을 찾아보게 되었다.

테스트해보자

Transactional(readOnly=true)

... o.s.orm.jpa.JpaTransactionManager : Creating new transaction with name [...UserService.getUserWithTransaction]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly
... o.s.orm.jpa.JpaTransactionManager : Opened new EntityManager [SessionImpl(1965184021<open>)] for JPA transaction
... o.s.orm.jpa.JpaTransactionManager : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@46199519]
Hibernate: 
    select
        u1_0.nickname 
    from
        users u1_0 
    where
        u1_0.nickname=?
... o.s.orm.jpa.JpaTransactionManager : Initiating transaction commit
... o.s.orm.jpa.JpaTransactionManager : Committing JPA transaction on EntityManager [SessionImpl(1965184021<open>)]
... o.s.orm.jpa.JpaTransactionManager : Closing JPA EntityManager [SessionImpl(1965184021<open>)] after transaction

no @Transactional

... tor$SharedEntityManagerInvocationHandler : Creating new EntityManager for shared EntityManager invocation
Hibernate: 
    select
        u1_0.nickname 
    from
        users u1_0 
    where
        u1_0.nickname=?

EntityManaging 같은 경우는 예상대로 동작하기는 했다.
데이터 모수가 작기는 했으나 실행 시간도 쪼끔의 차이가 있었다.
그럼 이제 그럼에도 불구하고 @Transactional(readOnly=true)를 써야하는 이유를 알아보기 위해 어떤 장점들이 있는지 더 알아보자.


@Transactional(readOnly=true)를 쓰면 적용되는 내용들이 뭐길래?

  1. hibernate의 세션 플러시 모드가 FlushType.MANUAL로 설정되어 Persistence Provider 가 EntityManager 를 닫을 때 dirty checking을 건너뛸 수 있도록 한다. (cpu 절약)
  2. org.hibernate.readOnlytrue 로 설정되므로 영속성 컨텍스트는 스냅샷을 보관하지 않는다. (메모리 절약)
  3. readOnly queryhint 적용을 통한 DB 레벨에서의 최적화가 이루어진다.

Spring read-only transaction Hibernate optimization
What are advantages of using @Transactional(readOnly = true)?
Is @Transactional(readOnly=true) a silver bullet?
@QueryHint의 readOnly 와 @Transaction의 readOnly 차이
@Transactional(readOnly = true)에 대한 질문입니다.


짧은 마무리

메모리나 성능 상에 큰 메리트가 있는 것도 아니고, 상황에 따라 readOnly 를 통해 설정되는 장점들도 많기 때문에 @Transactional(readOnly=true) 는 단순 단건 조회 등의 케이스를 제외하고는 웬만하면 붙이는 것이 좋을 것 같다.

  • Connector 과 DB Vendor 에 따라 어느 정도의 최적화가 이루어진다. (query hint를 통한 readOnly 설정, aurora의 경우 따로 설정 없이도 master-slave 쿼리 분산 등)
  • DB 에 따라 테이블 잠금을 생략하거나 실수로 트리거할 수 있는 쓰기 작업을 거부하는 용도로 사용할 수 있다.
  • 한 메서드 내에서 여러 조회를 하는 경우 매 select 쿼리마다 commit 하는 것을 방지할 수 있다. (1차 캐시는 보관하므로)
  • OSIV=false 인 경우 Transactional을 붙이지 않으면 조회 후 바로 준영속 상태가 되기 때문에 Lazy Loading을 시도할 경우 LazyInitializationException이 발생한다.
  • 아래와 같은 상황을 방지할 수 있다.

그리고 각 DB 의 readOnly 플래그에 대한 동작을 야물딱지게 정리한 글이 있어서 첨부하며 마무리한다.

DBMS 별 Transaction Read Only에 대한 동작 방식

TODO

Transactional 설정으로 인한 isolation level, propagation 기본값이 transactional이 없을 땐 어떻게 되는지?

Appendix.

[Baeldung] Using Transactions for Read-Only Operations

profile
💻 + ☕ = </>

0개의 댓글