Yomni's TIL Help

LazyConnectionDataSourceProxy 에 대하여 (동시성 이거 왜 돼요??)

1. LazyConnectionDataSourceProxy 란?

Spring에서 제공하는 LazyConnectionDataSourceProxy는 이름 그대로, 데이터베이스 연결을 Lazy하게 지연시키는 프록시 객체입니다.

기본적으로 Spring의 트랜잭션 처리 방식은 트랜잭션이 시작되자마자 DB 커넥션을 획득합니다. 하지만 LazyConnectionDataSourceProxy를 사용하면, 실제 SQL 실행 시점까지 DB 연결을 미룹니다.

즉, "트랜잭션은 열지만, 커넥션은 당장 열지 않는다"는 게 핵심.

2. 왜 쓰는가?

2.1 Spring 기반의 시스템에서 다음과 같은 상황이 발생할 수 있음:

  • 트랜잭션은 수백 개 열려 있음

  • 하지만 실제로 SQL을 수행하는 트랜잭션은 일부뿐

  • 그런데도 모든 트랜잭션이 DB 커넥션을 미리 가져가면?


    --> 💥 커넥션 풀 고갈 --> 병목 또는 장애 발생

2.2 LazyConnectionDataSourceProxy 도입 후 이점

  • 불필요한 커넥션 확보 X → 커넥션 풀 여유 확보

  • 읽기 전용 트랜잭션이라면 → 커넥션 자체 생략 가능

  • 트랜잭션 내에서 지연 확보 → 데드락이나 격리 이슈 감소

3. 삽질기: Repeatable read 격리 수준에서도 분산락과 함께 잘 작동한 이유

서비스 내부에서 두 개의 트랜잭션이 동시에 실행되는 구조였다.

  • 각 트랜잭션은 서로 다른 쓰기 작업을 수행함

  • 트랜잭션 격리 수준은 REPEATABLE_READ

  • 락은 Redis 분산락 기반으로 제어되고 있음

예상한 문제

  1. 트랜잭션 시작과 동시에 커넥션을 확보하면서,

  2. 락 획득 전에 DB lock이 걸림

  3. 결국 교착 상태(Deadlock) 발생

문제가 발생하지 않은 원인

  • Spring 은 트랜잭션을 시작하면 기본적으로 바로 커넥션을 얻음.(이라고 알고 있었음)

  • 분산락은 AOP 로 동작하는데, 디버그로 찍어보니 Transaction AOP 가 분산락 AOP 보다 먼저 적용됨

  • 따라서, Repeatable read 격리 수준에선 두개의 경합하는 트랜잭션이 분산락으로 순서가 보장되더라도,


    나중에 실행되는 트랜잭션에서 언두 영역을 Read 할 것이라 착각함

  • 하지만 B (나중에 실행되는) 트랜잭션에서 A 트랜잭션의 커밋한 내용을 읽었다??

--> 분산락 AOP 가 트랜잭션 AOP 보다 나중이라 하더라도 LazyConnectionDataSourceProxy 덕분에
실제 쿼리가 실행되는 시점에 트랜잭션이 시작됨

결과

  • 트랜잭션은 열려 있지만(논리적 트랜잭션) 커넥션은 아직 없음

  • 분산락을 먼저 잡고 --> LazyConnectionDataSourceProxy 에 의해 커넥션을 지연해서 획득한다

  • Repeatable_read 격리 수준이라도 데드락 없이 안전하게 동작한다.

한 줄 결론

Spring 트랜잭션은 추상화 정도가 높아 굉장히 유연하지만, 무조건 안전하진 않다.
(정확히는 추상화 정도가 높기 때문에 잘 모르고 쓰면 위험하다.)

LazyConnectionDataSourceProxy 는 이 커넥션 획득 타이밍을 실제 쿼리가 발생되는 시점까지 지연시키기 때문에
커넥션 획득 타이밍을 어느정도 제어할 수 있게 해주는 훌륭한 도구다.

특히 다음 조건이라면 도입을 적극 고려해봐야 한다.

  • 대규모 동시성 트랜잭션

  • 분산락을 사용하는 구조 (다중 인스턴스)

  • 연결 수가 한정된 환경 (RDS 연결 수 제한)

Last modified: 16 July 2025