🔐동시성 제어 정리 : 낙관적 / 비관적 / 분산 락
2025. 8. 5. 21:09

1. 동시성 제어 (Concurrency Control)

: 여러 사용자가 동시에 데이터에 접근하거나 조작할 때 발생할 수 있는 문제를 방지하기 위한 기술

  • 동시성 제어를 하지 않으면
    • 같은 데이터를 동시에 읽거나 수정할 때 데이터 정합성이 깨짐
    • 중복 처리, 데이터 충돌, 무결성 오류
    • 예 : 여러 사용자가 동시에 같은 열차 좌석을 예매하는 경우, 중복 예약이 발생함 
  • 주요 방법
    • 낙관적 락 (Optimistic Lock) : 충돌이 드물다고 가정하고, 마지막에 변경 여부 확인
    • 비관적 락 (Pessimistic Lock) : 충돌 가능성이 크다고 보고, 먼저 락을 걸고 작업 수행
    • 분산 락 : 여러 서버/인스턴스 환경에서 락을 제어

2. 낙관적 락 (Optimistic Lock)

  • 데이터 충돌이 거의 없을 것이라고 낙관적으로 가정하고 락을 사용하지 않음
  • 대신, 데이터를 읽은 시점과 수정 시점 사이에 다른 트랜잭션이 해당 데이터를 변경했는지 확인하여 충돌 감지
  • 동작 방식
    • 데이터를 읽을 때, 버전 번호(version)를 함께 읽음
    • 업데이트 시점에 해당 버전 번호가 변하지 않았으면 정상 처리
    • 바뀌었다면 OptimisticLockException 발생, 트랜젝션 롤백
  • 성능이 좋고 데드락 없음!
  • 충돌 시, 재시도 로직 필요함
  • 읽기 위주의 시스템. 충돌 가능성이 낮은 경우에 적합
  • JPA에서 사용 방법 : @version 필드를 이용하여 DB에서 충돌 여부 감지
// 엔티티에 버전 필드 추가
	@Version
    private Long version;

3. 비관적 락 (Pessimistic Lock)

  • 충돌이 자주 발생한다고 비관적으로 가정하고, 조회 시점부터 락을 걸어 다른 트랜젝션의 접근을 차단
  • 동작 방식
    • 데이터를 조회할 떄, DB 레벨 락(SELECT --- FOR UPDATE)을 걸어 다른 트랜젝션의 읽기/쓰기 차단
    • 락을 획득한 트랜잭션만 데이터 수정 가능
  • 장점 : 데이터 정합성 보장됨!!!
  • 단점 : 데드락 위험, 트랜잭션 병목 발생 가능
  • 충돌 가능성이 높은 경우, 반드시 정합성이 보장되어야 하는 경우에 적합
  • JPA에서 사용 방법 : SELECT --- FOR UPDATE와 같은 SQL로 DB의 row-level에 직접 락을 설정 
// Repository에 명시
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT p FROM Product p WHERE p.id = :id")
Product findByIdForUpdate(@Param("id") Long id);
  • JPA의 비관적 락 모드 종류
    • PESSIMISTIC_READ
      • 읽기 락. 다른 트랜잭션의 쓰기만 막음. (읽기는 허용)
      • 사용 목적 : 읽기 작업 중 데이터 수정 방지
      • 쿼리 예시 : SELECT ... FOR SHARE
    • PESSIMISTIC_WRITE 
      • 쓰기 락. 다른 트랜잭션의 읽기/쓰기 모두 차단
      • 사용 목적 : 정합성이 꼭 필요한 경우
      • 쿼리 예시 : SELECT ... FOR UPDATE
    • PESSIMISTIC_FORCE_INCREMENT
      • PESSIMISTIC_WRITE와 같지만, 버전 필드 자동 증가
      • 사용 목적 : 낙관적 락과 비관적 락을 혼합해 쓰고 싶을 때
      • 쿼리 예시 : SELECT ... FOR UPDATE + version++

4. 분산 락 (Distributed Lock) - Redis 기반

  • 여러 서버 인스턴스 환경에서 공유 자원을 제어할 때, 중앙 락 저장소(예:redis)를 통해 락을 설정하고 해제하는 방식
  • 동작 방식
    • Redis에 SETNX + EXPIRE 방식으로 락 key 생성
    • 락이 존재하면 다른 트랜젝션은 대기 또는 실패
    • 처리 완료 후 key 삭제로 락 해제
  • 장점 : 분산 시스템에서 자원 충돌 방지
  • 서버가 여러 대일 때, 중앙에서 락 제어 가능
  • TTL 설정 및 락 해제시 본인이 설정한 락인지 확인하는 로직 필요
  • Redis 외에도 Zookeeper, DB를 이용한 분산락 가능
  • Redisson 등의 라이브러리를 사용하면 재시도, Watchdog 기능 등 내장 지원 
LIST