어떤 개념일까?

Transactional(readOnly = true)

@Transactional의 한 속성으로, ”이 트랜잭션 안에서는 데이터를 읽기만 하고 변경하지 않는다”라는 것을 선언한다.

핵심은 강제가 아니라, 선언/힌트라는 점이다.

이 선언이 영속성 컨텍스트, JDBC, Connection, DB등에 각각 다른 신호를 보낸다.


어떤 문제를 해결하려고 나왔을까? 왜 사용 할까?

조회 전용 로직인데도 기본적으로 트랜잭션이 변경이 있을지 모른다고 가정한다면 불필요한 비용이 생긴다.

예를 들어서 JPA/Hibernate 환경에서는 다음과 같은 2가지 경우가 존재한다.

  • 더티 체킹 비용: 영속성 컨텍스트가 엔티티의 변경을 감지하려고 조회 시점의 시냅샷을 따로 저장해놓는다. 조회만 할 거라면 이 스냅샷도, flush 시점에 변경 비교도 불필요한 낭비가 된다.
  • 의도하지 않는 UPDATE: 조회만 하는 메서드에서 엔티티 필드를 건드리면 flush 시점에 UPDATE가 실행될 수 있다. 그래서 readOnly = true 라는 선언을 통해 구조적으로 예방해준다.

이렇듯, 조회 전용이라는 의도 선언을 통해서, 선은 최적화와 실수 방지를 위해서 존재한다.


어떻게 동작하나?

세 레벨로 이해가 빠르다.

  1. Hibernate/JPA 레벨에서 FlushModeMANUAL 로 바꿔서 자동 flush를 끈다. 그래서 더티 체킹용 스냅샷 비교가 생략되고, 변경이 있어도 DB로 쿼리가 나가지 않는다.
  2. JDBC Connection 레벨에서 Connection.setReadOnly(true)를 호출해 드라이버/DB에 읽기 전용 커넥션이라고 알린다. DB나 드라이버가 재량으로 최적화를 한다.
  3. 인프라 레벨에서 DB 라우팅을 쓰는 환경에서는 readOnly 플래그를 보고 읽기 전용 복제본으로 쿼리를 보내는 분기 기준으로 활용하기도 한다.

언제 쓰고, 언제 안 쓰나?

쓸 때:

  • 조회 전용 서비스 메서드
  • 여러 조회 쿼리가 한 메서드 안에서 묶여 “조회 사이의 일관된 스냅샷”이 필요할 때, 트랜잭션으로 격리 수준을 보장해준다.

안 쓸 때:

  • INSERT/UPDATE/DELETE가 한번이라고 섞여있는 메서드
  • 트랜잭션 자체가 굳이 필요없는 단발성 조회, JdbcTemplate에서는 단순 SELECT 하나면 트랜잭션을 걸지 않아도 된다.

추가 궁금한 질문들

  • DB가 setReadOnly(true)를 실제로 어떻게 처리하는가? (드라이버별/DB별로 무시하는 경우가 많은데, 그럼 커넥션 힌트의 신뢰도는?)