피드로 돌아가기
강남언니 공식 블로그Backend
원문 읽기
여러개의 DB 작업을 동시에 수행하고 결과를 합쳐서 보내줘야 할 때
Spring에서 Hibernate를 사용한 다중 DB 조회를 @Async + @Transactional(Propagation.NOT_SUPPORTED)로 병렬 처리하여 응답시간 7초에서 1초로 단축
AI 요약
Context
메인페이지 위젯 데이터를 로드할 때 각 위젯마다 복잡한 Hibernate 쿼리를 순차적으로 실행하면서 7초 이상의 응답지연이 발생했다. 여러 DB 작업을 동시에 수행한 뒤 결과를 모아서 한 번에 응답해야 하는 상황이었다.
Technical Solution
- CompletableFuture 시도: CompletableFuture.runAsync()와 allOf().join()으로 비동기 처리를 시도했으나, Hibernate의 synchronized transaction session이 ThreadLocal을 벗어난 스레드에서 얻어질 수 없어 HibernateException 발생
- @Async 어노테이션 적용: 프록시 기반 비동기 처리를 위해 WidgetAsyncService를 별도 서비스로 분리하고 @Async 메서드 작성했으나, 트랜잭션 동기화 문제로 실패
- Transaction Propagation 설정 변경: @Async 메서드에 @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true) 적용으로 부모 트랜잭션에서 벗어나 새로운 트랜잭션 세션 획득 가능하게 변경
- 타스크 실행자 및 커넥션풀 튜닝: Executor 스레드 수와 Hibernate 커넥션풀 크기를 위젯 개수 이상으로 설정하여 완전한 병렬 처리 확보
- Future 객체 수집 및 대기: 비동기 메서드 호출 결과를 List에 수집하고 future.get()으로 모든 작업 완료를 명시적으로 대기
Impact
- 응답시간 7초 이상에서 1초로 단축 (7배 이상 개선)
- 캐싱 추가 적용 후 캐시 히트 시 200ms 수준으로 추가 개선
Key Takeaway
Hibernate를 사용하는 Spring 애플리케이션에서 비동기 DB 조회를 구현할 때 TransactionSynchronizationManager의 트랜잭션 바인딩 메커니즘을 이해하고 Propagation.NOT_SUPPORTED로 트랜잭션 격리를 명시적으로 선언하면 ThreadLocal 바깥의 스레드에서도 정상적인 세션 획득이 가능하다.
실천 포인트
Hibernate를 ORM으로 사용하는 Spring 서비스에서 다중 DB 조회를 병렬화해야 할 때, @Async + @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true) 조합을 별도 서비스 클래스의 메서드에 적용하고, Executor 스레드 풀 크기와 DB 커넥션풀 크기를 동시 작업 수 이상으로 설정하면 7배 이상의 응답시간 단축을 달성할 수 있다.