카테고리 없음

[JPA] 왜 @Transactional(readOnly = true) 를 '그냥' 사용해?

wonow_ 2024. 5. 12. 14:53

왜 @Transactional(readOnly = true) 를 '그냥' 사용하는가??

JPA 를 처음 배울 때 @Transactional(readOnly = true) 를 왜 쓰냐고 물어보면 '그냥 블로그에서 봤는데 이렇게 쓰면 조회성능이 좋아진대요, 그래서 기본으로 깔고 들어가면 좋대요' 라는 답변이 돌아왔다.

그래서 '그냥' 사용 해보려고 했었는데 뭔가,, 그냥이라는 말이 걸려서 조회 메서드는 @Transactional 자체를 안 붙여도 되지 않을까? 생각했었다. 그래서 조회 메서드는 @Transactional 자체를 안 붙이고 진행 했었다.

근데 조회 메서드는 readOnly = true 를 '그냥' 일단 깔고 들어가야 된다 라고 한다.. 흠...

왜 '그냥' 일까..?

 

일단 이유가 뭔데?

영속성 컨텍스트(Persistence Context), 변경 감지(Dirty Checking) 와 연관이 있다.

 

Snapshot 과 변경 감지

JPA는 엔티티를 불러올 때 영속성 컨텍스트의 Snapshot 영역에 엔티티의 초기 상태의 정보를 저장한다.

이후 Flush 를 호출할 때 엔티티가 수정되었다면 쓰기 지연 저장소에 수정 쿼리를 저장한 후 Commit 한다.

이를 Snapshot을 통한 변경 감지라고 한다.

 

예전에 TIL 간단하게 포스팅 한 게 있는데 읽으면 도움 될 듯하다..

 

TIL 2023-11-08 영속성 컨텍스트, Jpa의 트랜잭션

영속성객체의 생명이나 공간을 자유롭게 유지하고 이동할 수 있는 객체의 성질 Entity 객체들은 영속성 컨텍스트에 의해 관리가 된다. Entity Manager ( = EM )Entity Manager Factory ( = EMF )글쓰는데 편의를

wonowdaily.tistory.com

 

그래서 Snapshot 은 무슨 상관이야?

@Transactional(readOnly = true) 를 설정하면 이는 JPA가 조회용 메서드을 인식하고 Snapshot을 따지 않는다.

조회용 메서드는 엔티티가 수정될 이유가 없기 때문에 애초에 Snapshot을 따지 않는 것이다.

그래서 메모리 상에서 이점을 볼 수 있다.

 

...수정될 필요가 없다?

 

OSIV 패턴(Open Session in View Pattern) : Hibernate 세션 플러시 모드

Hibernate 세션 플러시에는 여러가지 모드가 있다. 일반적으로 JPA는 Hibernate를 구현체로 사용하기 때문에 Hibernate로 설명한다.

 

Hibernate의 Flush 타이밍

  • 일반적으로 Transaction 이 Commit 될 때 Session 의 모든 변경사항이 DB 로 Flush 된다.
  • Hibernate 는 다음과 같은 경우 Session 을 DB에 Flush 한다.
    • flush() 를 명시적으로 호출하는 경우
    • Hibernate Transaction 이 Commit 되는 경우
    • 쿼리를 실행하기 전, 영속성 컨텍스트의 상태가 쿼리 결과에 영향을 미친다고 판단되는 경우
  • 사용자는 위 Flush 시점을 커스텀 할 수 있다.

setFlushMode()

  • setFlushMode() 메서드를 사용하면 Flush 모드를 변경할 수 있다.
  • Hibernate 의 Session Flush 모드는 4가지다
    • 기본값은 AUTO
    • ALWAYS, AUTO, COMMIT, MANUAL 이 있다.

ALWAYS

  • 모든 쿼리를 실행하기 전 영속성 컨텍스트를 Flush 한다.

AUTO

  • flush() 를 명시적으로 호출하는 경우
  • Hibernate Transaction 이 Commit 되는 경우
  • 쿼리를 실행하기 전, 영속성 컨텍스트의 상태가 쿼리 결과에 영향을 미친다고 판단되는 경우

COMMIT

  • flush() 를 명시적으로 호출하는 경우
  • Hibernate Transaction 이 Commit 되는 경우

MANUAL

  • flush() 를 명시적으로 호출하는 경우

 

@Transactional(readOnly = true) 를 설정하면 Flush 모드를 MANUAL 로 설정한다.

트랜잭션 내에서 flush()를 호출하지 않는 한, 수정 내역은 DB에 적용되지 않는다.

즉, Flush() 를 호출하지 않으니 예상치 못한 수정을 방지할 수 있다.

 

DB Replication 개발론

Replication 은 Master - Slave 구조로 복제본 DB를 운용하여, Master DB의 장애 발생 시 Slave DB를 Master DB로 승격시켜 장애를 빠르게 복구할 수 있는 개발론이다. 조회 작업은 Slave DB에서 수행하고 수정 작업은 Master DB에서 수행하여 DB 커넥션 측에서 트래픽을 분산 할 수 있다.

 

이런 구조를 가져가면 readOnly = true 가 설정돼있는 메서드는 Slave DB에서 가져가게끔 하면 좋지 않을까?

 

...DB 커넥션?

 

DB 커넥션

@Transactional 을 붙이면 해당 메서드가 종료될 때까지 DB 커넥션을 유지한다.

하지만 사용자가 많게 된다면 커넥션을 오래 붙잡고 있는 것에 대해 결정해야 한다.

메모리상 이점을 못보더라도 DB 커넥션이 따른 곳에서 필요하면, 커넥션을 끊고 애플리케이션단에서 로직을 수행한다음 마지막에 Commit 만 해주면 되지 않을까? 라는 생각도 있다.

 

결론

'그냥' 사용하지 말고 적절히 트레이드 오프하면서 사용해야 된다.

메모리, DB 커넥션에 따라 결정하는 게 좋다.