어떤 개념일까?
Transactional(readOnly = true)
@Transactional의 한 속성으로, ”이 트랜잭션 안에서는 데이터를 읽기만 하고 변경하지 않는다”라는 것을 선언한다.
핵심은 강제가 아니라, 선언/힌트라는 점이다.
이 선언이 영속성 컨텍스트, JDBC, Connection, DB등에 각각 다른 신호를 보낸다.
어떤 문제를 해결하려고 나왔을까? 왜 사용 할까?
조회 전용 로직인데도 기본적으로 트랜잭션이 변경이 있을지 모른다고 가정한다면 불필요한 비용이 생긴다.
예를 들어서 JPA/Hibernate 환경에서는 다음과 같은 2가지 경우가 존재한다.
더티 체킹 비용: 영속성 컨텍스트가 엔티티의 변경을 감지하려고 조회 시점의 시냅샷을 따로 저장해놓는다. 조회만 할 거라면 이 스냅샷도, flush 시점에 변경 비교도 불필요한 낭비가 된다.의도하지 않는 UPDATE: 조회만 하는 메서드에서 엔티티 필드를 건드리면 flush 시점에 UPDATE가 실행될 수 있다. 그래서 readOnly = true 라는 선언을 통해 구조적으로 예방해준다.
이렇듯, 조회 전용이라는 의도 선언을 통해서, 선은 최적화와 실수 방지를 위해서 존재한다.
어떻게 동작하나?
세 레벨로 이해가 빠르다.
- Hibernate/JPA 레벨에서
FlushMode를MANUAL로 바꿔서 자동 flush를 끈다. 그래서 더티 체킹용 스냅샷 비교가 생략되고, 변경이 있어도 DB로 쿼리가 나가지 않는다. - JDBC Connection 레벨에서
Connection.setReadOnly(true)를 호출해 드라이버/DB에 읽기 전용 커넥션이라고 알린다. DB나 드라이버가 재량으로 최적화를 한다. - 인프라 레벨에서 DB 라우팅을 쓰는 환경에서는 readOnly 플래그를 보고 읽기 전용 복제본으로 쿼리를 보내는 분기 기준으로 활용하기도 한다.
언제 쓰고, 언제 안 쓰나?
쓸 때:
- 조회 전용 서비스 메서드
- 여러 조회 쿼리가 한 메서드 안에서 묶여 “조회 사이의 일관된 스냅샷”이 필요할 때, 트랜잭션으로 격리 수준을 보장해준다.
안 쓸 때:
- INSERT/UPDATE/DELETE가 한번이라고 섞여있는 메서드
- 트랜잭션 자체가 굳이 필요없는 단발성 조회, JdbcTemplate에서는 단순 SELECT 하나면 트랜잭션을 걸지 않아도 된다.
추가 궁금한 질문들
- DB가
setReadOnly(true)를 실제로 어떻게 처리하는가? (드라이버별/DB별로 무시하는 경우가 많은데, 그럼 커넥션 힌트의 신뢰도는?)