08_Redis를_활용한_분산락 - loveAlakazam/hh-08-concert GitHub Wiki

목적

기존 시스템에서의 문제점

  • 낙관적락의 특징과 한계
    • 방식: 데이터 갱신 시점에서 버젼을 체크한다.
    • 충돌처리: 갱신시 version이 다르면 예외가 발생하여 rollback됨.
    • 장점: 실제락이 없더라도 동시성처리가 가능.
    • 단점: 동시요청이 많을경우 충돌이 빈번해져서 실패율이 높아짐.
    • 한계: 단일 애플리케이션 내 트랜잭션 범위에서만 유효함.
      • 네트워크 분산환경에서는 낙관적락 자체가 적용되지 않음.
      • 애플리케이션 서버 여러대가 동시 요청하면, 데이터베이스만으로 충돌을 막기가 어려움.
      • 동시에 수정이 발생할경우에는 적합하지 않다.
  • 비관적락의 특징과 한계
    • 방식: 트랜잭션 시작지점에서 해당 row에 대하여 DB 락을 획득함.
    • 충돌처리: 하나의 트랜잭션만 접근가능하며, 나머지는 대기하거나 타임아웃발생
    • 장점: 정합성 강력 보장하며 동시에 수정불가
    • 단점: 성능저하, DeadLock 발생위험, 스케일확장에 약함.
    • 제한: DB 수준에서만 유효하고 서비스간 요청 충돌제어는 불가능.
      • DB락이기때문에 트랜잭션 scope내부에만 제한.
      • 동시에 다른 노드에서 같은 사용자에 대한 로직을 실행하면 동시요청을 막지 못함.
      • DB row를 물리적으로 잠금으로써 다른트랜잭션은 대기해야되므로 트래픽이 많거나 병렬처리가 많은경우에는 병목현상이 발생할 수 있다.

낙관적락, 비관적락의 사용은 공유자원을 읽기/쓰기 접근이 동시에 요청할경우 동시에 실행되면서 공유자원의 값의 일관성이 깨지는 현상인 Race condition 발생을 해결해준다. 하지만 이는 단일인스턴스에서만 가능하며 락의 대상이 데이터베이스의 row로 제한되어있다.

포인트충전, 포인트 사용의 경우에는 '포인트'가 공유자원이며, 유저포인트에 대한 데이터베이스 row 이다.

동시에 좌석 예약의 경우에는 '좌석'이 공유자원이며, 해당 좌석에 대한 데이터베이스 row이다.

하지만 예약결제는 공유자원이 데이터베이스에 존재하지 않는다. 동시에 예약결제를 할경우에는 트랜잭션 scope만으로는 해결하기가 어렵다.

요약

  • 애플리케이션 서버가 1대일때는 가능하지만, 멀티인스턴스 서버에서의 동시요청을 막지못한다.

    • 낙관적락, 비관적락만으로는 한개의 서버에서만 작업하므로 다른 서버의 요청은 막을 수 없다.
  • 락의 대상이 데이터베이스가 아닌 경우

  • 비관적락은 성능에 부담을 줄 수 있고, 낙관적락은 변경이 잦을경우 버젼충돌이 빈번하여 성능이 저하될 수 있다.

Redis 를 활용한 분산락이 왜 필요할까?

  • 동시요청 자체를 차단하며 데이터의 정합성을 지킬 수 있다.

  • 여러 인스턴스에서 공유자원을 동시에 접근을 하려고할때 동시성제어

  • 락의 대상이 데이터베이스가 아닌경우에 가능하다

  • key-value기반의 원자성을 이용하여, DB 성능부하를 최소화시킨다

분산락의 종류

심플락

정의

한번에 한 프로세스만 특정 key에 대해서 작업할 수 있도록 제어하는 가장 기본적인 형태의 분산락이다.

락을 얻은 클라이언트가 일정 시간동안 자원에 접근할 수 있도록 제한하고, 시간이 지나면 자동으로 락이해제되므로 데드락 방지에 효과적이다.

특징

  • Redis의 SET NX 명령어(Set if Not eXists) 를 주로 사용

  • 먼저 락을 얻으면 일정시간을 독점하는 선점형락이다.

  • 락 획득 실패시 대기없이 바로 실패되며, 예외처리

  • 만료시간(TTL)을 함께 설정하여 데드락 방지.

  • 구현이 간단하고 가장 보편적으로 사용된다.

  • 중복 요청을 차단하며 빠른 응답

사용사례

  • 재고차감
  • 결제 승인처리
  • 간단한 리소스 예약 시스템
  • 동시성이 낮은 환경에서의 작업 스케줄링
  • 캐시 업데이트시 경쟁 상태를 방지

스핀락

정의

락 획득 실패시 일정 시간의 대기(sleep)을 한후에 반복적으로 락획득을 시도하는 방식이다.

락을 얻지 못하면 잠시 기다린 후 재시도를 하는 구조로, 마치 CPU가 루프를 돌며 락을 계속 감시하는 모습과 유사해서 "spin"이라는 이름이 붙여짐.

특징

  • 백오프(backoff)알고리즘을 활용하여 점진적으로 재시도 간격을 늘릴 수 있다.
  • 적극적인 대기 전략으로 락이 빨리 해제될 것으로 예상될때 효율적이다.
  • 시스템 자원(CPU)를 많이 소비할 수 있다.
  • 재시도 횟수 제한을 두어 무한대기를 방지해야한다.

사용사례

  • 짧은 임계구역작업
  • 높은 경쟁상황에서 락획득 필요시
  • 마이크로서비스 간 짧은 시간내 완료되는 작업조율
  • 일시적인 네트워크 지연에 대응이 필요한 경우
  • 락을 꼭 얻어야할 경우, 락의 대기시간이 짧고 충돌이 자주 일어나지 않을경우

pub/sub 락

정의

Redis의 발행/구독(Publish/Subscribe) 메커니즘을 활용하여 락의 해제를 구독자에게 알려주는 방식

특징

  • 락 해제시 채널에 메시지를 발행하여 대기중인 클라이언트에게 알려준다.
  • 능동적 폴링(polling)이 필요없어서 리소스 사용량이 감소된다.
  • 락 획득 시도시 일반적으로 심플락을 사용하고, 실패시 구독모드로 전환한다.
  • 구현이 복잡하지만 효율적인 대기 메커니즘을 제공한다.

사용사례

  • 장시간 실행되는 작업에 대한 락 관리
  • 고부하 환경에서 폴링으로 인한 부하를 줄여야할경우
  • 실시간성이 중요한 분산시스템
  • 다수의 클라이언트가 동일한 리소스에 경쟁적으로 접근하는 경우

해결방안

분산락 적용 계획

좌석 예약

포인트 충전 / 포인트 사용

예약 결제


한계점 및 고민

스케일링 제한

  • 단일 인스턴스의 처리량(throughput)에 제한이 있어서 고부하 환경에서는 병목현상이 발생할 수 있다.
  • 락 정보를 저장하는 메모리에 제한이 있다.

네트워크 신뢰성 의존

분산락은 락정보를 Redis 같은 외부 중앙저장소에 저장하기 때문에 끊김, 분할 현상이 발생하면 락의 정확성이 깨질 수 있다.

  • 클라이언트와 Redis 서버간의 네트워크 지연으로 인해 락 타임아웃이 부정확해질 수 있다.
  • 다른 노드들의 시간 동기화 문제(클록드리프트)로 인해 락 만료시간이 예상과 다르게 동작할 수 있다.
  • 네트워크 불안정성으로 인해 락획득/락해제 과정이 중단될 위험이 있다.

결론

  • 레디스를 활용하여 분산락을 구현해봤습니다.