lock 관련 정리 사항들

김형준·2025년 2월 13일
0

Redisson의 lock 획득 대기

Redisson에서는 lock 취득 실패 스레드들은 Redis pub/sub과 spin lock이 혼합된 방식으로 lock을 얻을 수 있을 때 까지 대기한다.

따라서 Lettuce를 사용할 때와 달리 아래와 같은 lock 대기 코드를 구현하지 않아도 된다.

while (!lockRedisRepository.acquireLock(lockKey, Duration.ofMillis(30000))) {
    try {
        Thread.sleep(100); //100ms
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new IllegalStateException("예매 프로세스가 중단되었습니다.", e);
    }
}

Lock 해제 방식들

기본 동작 방식

try-finally 구조로 lock 해제 시 메서드 실행이 끝나는 지점에서 바로 해제된다.

반면 TransactionSynchronizationManager.registerSynchronizationafterCompletion()로 lock 해제 시 메서드 종료 이후 트랜잭션이 끝나는 시점에 맞춰 lock이 해제된다.

@Transactional 사용 시 AOP 프록시 내부에서는 아래와 같은 순서로 트랜잭션 흐름이 이루어진다.

  1. 트랜잭션 시작
  2. 비즈니스 로직(메서드) 수행
  3. 메서드 종료
  4. 트랜잭션 처리
    1. 이 때 commit, rollback 처리가 이루어짐
  5. 트랜잭션 종료

이 흐름에서 try-finally 구조는 3번 완료 시점에 lock을 해제하며, afterCompletion()은 4번 완료 시점에 lock을 해제한다.

큰 차이는 아니라고 생각될 수도 있다.

하지만 이로 인해 트랜잭션이 commit 되기 전에 다른 스레드가 lock을 잡아 작업할 수 있게 되며, 이로 인해 동시성 문제가 발생할 수 있다.

따라서 afterCompletion()을 적용해 동시성 문제를 예방할 수 있다.

read/write lock 사용 이유 정리

1. 조회가 많이 발생하는 경우

조회 작업들은 서로 간섭하지 않으므로 쓰기 작업의 lock을 조회 시에도 사용할 필요가 적다.

따라서 쓰기 작업이 자주 발생하지 않고, 조회가 빈번하게 발생하는 경우 read/writeLock을 분리해서 사용하면 경합을 줄이고 전체 시스템의 throughput을 늘릴 수 있다.

2. 쓰기 작업이 많이 발생하는 경우

읽기 락을 제대로 활용하기 힘들어지고, read/write lock을 구분하기 위한 추가적인 오버헤드의 부담이 일반 lock에 비해 더 크게 다가올 수 있다.

또한 writeLock 위주로만 사용되면 throughput 증가량이 체감되기 힘들고 사실상 단일 lock과 비슷하게 동작한다.

참고: https://incheol-jung.gitbook.io/docs/q-and-a/spring/redisson-trylock

0개의 댓글

관련 채용 정보