[JPA] 동시성 제어 : LockModeType.PESSIMISTIC_WRITE

2025. 12. 7. 16:13·ServerDev/SpringBoot

Spring Data JPA를 사용하여 동시성 이슈를 해결하려 할 때, 가장 강력하면서도 확실한 방법인 LockModeType.PESSIMISTIC_WRITE(비관적 쓰기 락)에 대해 정리해 봅니다.

1. PESSIMISTIC_WRITE란?

LockModeType.PESSIMISTIC_WRITE는 데이터베이스의 배타적 락(Exclusive Lock) 기능을 사용하여 동시성을 제어하는 방식입니다.
쉽게 비유하자면 다음과 같습니다.

"내가 이 데이터를 다 쓰고 나갈 때까지, 아무도 들어오지 마! (읽지도 말고 쓰지도 마!)"

트랜잭션이 시작되고 데이터를 조회하는 순간부터 락을 걸어버리기 때문에, 데이터의 무결성이 깨질 확률을 '0'에 수렴하게 만드는 아주 강력한 옵션입니다. (대신 해당 lock 이 풀리기 전까진 다른 유저가 접근을 못함.)

 

"왜 내 요청만 멈춰 있을까?"

위 다이어그램은 비관적 락(PESSIMISTIC_WRITE) 이 걸려 있는 상황에서 여러 트랜잭션이 동시에 들어왔을 때 벌어지는 일을 시각화한 것입니다. 하나씩 뜯어서 상황을 살펴봅시다.

Transaction A (현재 처리 중인 승리자)

  • 상황: 가장 먼저 도착한 Transaction A가 데이터(Database Row)에 접근하여 열쇠(Exclusive Lock)를 획득했습니다.
  • 상태: A가 작업을 모두 마치고 커밋(Commit)할 때까지 이 데이터는 오직 A만의 것입니다.
  • 비유: 화장실이 딱 하나뿐인 카페에서 A가 문을 잠그고 들어간 상태입니다.

Transaction B, C, D ... (문 밖의 대기자들)

  • 상황: Transaction A가 아직 일을 끝내지 않았는데, 뒤이어 B, C, D가 들어왔습니다.
  • 동작: 이미 자물쇠(Lock)가 채워져 있기 때문에, DB는 이들의 접근을 차단(Blocked) 합니다.
  • 결과: 에러가 나고 튕겨 나가는 것이 아니라, "자리가 날 때까지 무한 대기(Waiting for Lock)" 상태에 빠집니다.

"로딩(Loading...)"의 정체

  • 사용자 화면에서 뱅글뱅글 도는 로딩바의 정체가 바로 저 Waiting for Lock 구간입니다.
  • 문제는 단순히 기다리는 것에서 끝나지 않습니다. Transaction B, C, D는 대기하는 동안 데이터베이스 커넥션(DB Connection)을 하나씩 붙잡고 놓아주지 않습니다.

결론: 트래픽이 몰리면?

  • 대기열이 길어질수록 뒤에 있는 Transaction Z는 앞사람들이 다 끝날 때까지 한참을 기다려야 합니다.
  • 결국 DB 커넥션 풀(Connection Pool)이 바닥나면서, 락과 상관없는 다른 기능까지 덩달아 멈추거나 서버 전체가 다운되는 장애로 이어질 수 있습니다.

2. 동작 원리 (SELECT ... FOR UPDATE)

이 옵션을 적용한 Repository 메서드를 실행하면, JPA는 데이터베이스에 다음과 같은 쿼리를 날립니다.

SELECT * FROM product 
WHERE id = 1 
FOR UPDATE;  -- 핵심: 이 구문이 붙으면서 배타적 락을 획득함
  • 락 획득: 트랜잭션 A가 이 쿼리를 실행하면 해당 Row(행)에 대한 열쇠(Lock)를 획득합니다.
  • 타 트랜잭션 차단: 트랜잭션 B, C가 같은 데이터를 수정하려고 접근하면, A가 커밋(Commit)하거나 롤백(Rollback)해서 열쇠를 반납할 때까지 무한정 대기(Waiting) 상태에 빠집니다.

3. 코드 적용 예시

public interface ProductRepository extends JpaRepository<Product, Long> {

    @Lock(LockModeType.PESSIMISTIC_WRITE) // 비관적 락 적용
    @Query("select p from Product p where p.id = :id")
    Optional<Product> findByIdWithPessimisticLock(@Param("id") Long id);
}

4. 치명적인 단점: 대규모 트래픽에서의 병목 현상

데이터의 정합성(동시성 제어) 측면에서는 완벽하지만, 성능 측면에서는 양날의 검입니다.

사용자가 적을 때는 문제가 없지만, 접근 유저가 급증하는 상황(수강 신청, 선착순 이벤트 등)에서는 심각한 성능 저하가 발생합니다.

  • 상황: 1개의 Row에 100명의 유저가 동시에 접근.
  • 현상:
    1. 1번 유저가 락을 잡고 로직을 수행 (약 0.1초 소요 가정).
    2. 나머지 99명은 DB 앞에서 줄 서서 대기 (DB Connection 점유).
    3. 뒤쪽 순번의 유저들은 앞사람들이 끝날 때까지 기다리느라 화면이 멈춤 (Loading...).
    4. 결국 DB 커넥션 풀(Connection Pool)이 고갈되거나 타임아웃(Timeout) 에러 발생.

즉, "줄 세우기"를 너무 확실하게 하다 보니 처리 속도가 느려지는 병목(Bottleneck) 현상이 생기는 것입니다.

5. 해결 방안 및 향후 계획 (Native DB / QueryDSL 등)

현재 구조에서는 PESSIMISTIC_WRITE가 데이터 꼬임 방지에는 탁월하지만, 트래픽이 몰릴 경우 서비스 전체가 느려질 위험이 있습니다.

따라서 트래픽이 증가할 것을 대비하여 다음과 같은 개선이 필요할 수 있습니다.

  1. Native Query (Native DB) 사용: JPA의 오버헤드를 줄이고 DB 고유의 기능을 직접 사용하여 쿼리 최적화.
  2. 분산 락 (Redis): DB에 부하를 주지 않고 Redis를 통해 락을 관리하여 DB 커넥션 고갈 방지.
  3. 낙관적 락 (Optimistic Lock): 락을 걸지 않고 버전(Version) 관리를 통해 충돌 발생 시에만 재시도 처리 (충돌이 잦지 않은 경우 유리).

결론

PESSIMISTIC_WRITE는 '속도보다는 데이터의 정확성이 절대적으로 중요할 때(예: 계좌 잔액 차감)' 사용하는 최후의 보루와 같습니다. 하지만 고성능/대용량 트래픽 환경에서는 DB 락이 병목 지점이 될 수 있으므로, 서비스 규모에 맞춰 적절한 락 전략을 선택하거나 Native 레벨의 튜닝을 고려해야 합니다.



 

'ServerDev > SpringBoot' 카테고리의 다른 글

[Spring Boot] 사용자/관리자 API 분리를 위한 멀티모듈 설계 실전 가이드 (Common, User, Admin)  (0) 2025.12.13
[SpringBoot & FormData] JSON + 파일 업로드: HttpMediaTypeNotSupported 에러 해결  (0) 2025.12.09
[SpringBoot&JPA] QR 코드 기반 게스트 계정 자동 생성 및 로그인 구현  (0) 2025.12.07
[Spring Boot] 권한 검증 - 커스텀 AOP와 Redis로 해결하기 (@IsCouncil)  (0) 2025.12.06
[SpringBoot - SSR] 파일 업로드 에러: "Failed to convert String to MultipartFile" 원인과 해결  (0) 2025.12.06
'ServerDev/SpringBoot' 카테고리의 다른 글
  • [Spring Boot] 사용자/관리자 API 분리를 위한 멀티모듈 설계 실전 가이드 (Common, User, Admin)
  • [SpringBoot & FormData] JSON + 파일 업로드: HttpMediaTypeNotSupported 에러 해결
  • [SpringBoot&JPA] QR 코드 기반 게스트 계정 자동 생성 및 로그인 구현
  • [Spring Boot] 권한 검증 - 커스텀 AOP와 Redis로 해결하기 (@IsCouncil)
yeseul-kim01
yeseul-kim01
  • yeseul-kim01
    슬 개발일지
    yeseul-kim01
  • 전체
    오늘
    어제
    • 분류 전체보기 (79)
      • 자격증 (1)
        • 정보보안기사 (0)
      • DevOps (17)
        • Docker (6)
        • Kubernetes (1)
        • GitHub Actions (0)
        • AWS (4)
        • Monitoring (1)
        • Nginx (1)
        • GCP (3)
      • ServerDev (34)
        • SpringBoot (13)
        • DJango (5)
        • FastAPI (14)
        • Next (0)
        • Flask (0)
        • Database (2)
      • Algorithm (2)
        • BFS (0)
        • DFS (1)
        • 다익스트라 (0)
      • CS (8)
      • Data Engineering (1)
      • AI&MLOps (2)
      • Architecture (6)
      • Software Engineering (0)
        • Library Packaging (0)
      • Project (5)
        • docx-generator (0)
        • speak-note (2)
        • ms-serving (1)
        • keyshield (2)
      • ProgrammingLanguages (3)
        • Python (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    Django
    실시간시스템
    grpc
    FastAPI
    실무일기-인프라편
    하이브리드아키텍처
    실무일기-백엔드편
    동시성제어
    아키텍처
    FastAPI - CORS 마스터
    STT
    KServe
    MLops
    프로젝트기록-speaknote
    NLP부트캠프
    트러블슈팅
    프로젝트기록-KeyShield
    multipartfile
    SpringBoot
    비동기처리
    di
    Kubernetes
    아키텍처설계
    SpeakNote
    멀티모듈
    rag
    depends
    KeyShield
    docker
    백엔드
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
yeseul-kim01
[JPA] 동시성 제어 : LockModeType.PESSIMISTIC_WRITE
상단으로

티스토리툴바