~/tech-blog/posts/distributed-lock — zsh

DB 분산락

이전에 비관적 락에 대해 작성하였습니다.

DB 비관적 락

비관적 락을 요약하자면 아래와 같습니다.

  • 모든 트랜잭션에 충돌이 날 것이라고 가정하고, 우선적으로 Lock을 점유하여 다른 트랜잭션이 접근하지 못하게 하는 방법입니다.

  • 데이터를 수정할 때 x-Lock을 점유하고 있으므로 충돌이 발생할 가능성이 적습니다.

  • 사용자가 수정을 마칠 때까지 다른 사용자는 **스핀락**으로 대기하고 있습니다.

    이 때문에 비관적 락의 경우 Time-out 설정을 통해 언제까지 대기할 것인지 꼭 지정해줘야 합니다.

  • 비관적 락의 경우 next-Lock(갭락)을 사용하기 때문에, 갭락이 무엇이고 어떻게 동작하는지 알아두면 비관적 락을 사용할 때 큰 도움이 될 것입니다.

    • 갭락 혹은 next-lock은 레코드 자체에 락을 거는 것뿐만 아니라 해당 레코드의 앞뒤 "갭"에도 락을 거는 방식입니다. 이는 트랜잭션이 실행되는 동안 새로운 레코드가 해당 범위에 삽입되는 것을 방지합니다. [갭락 설명 블로그 : https://idea-sketch.tistory.com/46]

분산락이란?

  • 분산락이란 멀티스레드 환경에서 공유 자원에 접근할 때 데이터의 정합성을 지키기 위해 사용합니다.
  1. 어노테이션 기반의 Redisson을 구현하여 사용하는 방법

그렇다면 왜 분산락을 사용하는 것일까요?

  1. **비관적 락**은 단일 데이터베이스 인스턴스 내에서 동시성 문제를 해결하는 데 적합하지만, 분산 환경에서의 여러 서버와 DB 간의 동시성은 해결해주지 못합니다. 이 때문에 분산 환경에서도 동시성을 해결하기 위해 **분산락**을 사용합니다.
    1. 분산 환경에서는 네트워크 지연, 패킷 손실, 장애 등이 발생할 경우 락을 유지하기가 어렵기 때문입니다.
    2. 비관적 락을 사용하면 락에 접근하려는 모든 노드가 대기 상태가 되면서 스핀락에 의해 병목현상이 발생할 수 있습니다.
    3. 위와 같은 상황 때문에 분산 환경에서 지연이 발생하고, 이것이 누적되면 큰 문제로 이어질 수 있습니다.

락 획득 방식

  • 비관적 락의 획득 방식 [스핀락] : 앞서 **비관적 락**은 **스핀락**으로 락을 획득한다고 하였습니다. 이 때문에 락이 풀릴 때까지 계속 시도함으로써 CPU 시스템 자원이 비효율적으로 사용됩니다.

  • Redisson의 분산 락의 획득 방식 [pub/sub] :

    1. 락 획득 시도
    • MySQL에서는 SELECT ... FOR UPDATE와 같은 쿼리를 사용하여 특정 행에 대해 락을 시도합니다. 만약 다른 트랜잭션이 이미 해당 행에 대해 락을 걸었다면, 현재 트랜잭션은 락이 해제될 때까지 대기 상태에 들어갑니다.
    • Redisson에서 락을 시도할 때, 클라이언트는 Redis에 특정 키(락을 나타내는)를 사용하여 SETNX 명령을 보냅니다. 이 명령은 "키가 존재하지 않으면 생성하고, 존재하면 아무것도 하지 않는다"는 의미입니다. SETNX 명령이 성공하면 클라이언트는 락을 획득하게 됩니다. 실패하면 락을 획득하지 못하고 대기 상태로 들어갑니다.
2. Pub/Sub 채널 구독 설정

- 락을 보유한 트랜잭션이 완료되거나 롤백되면, MySQL은 락을 자동으로 해제하고 대기 중인 트랜잭션 중 하나가 락을 획득하게 됩니다.
- 락을 보유한 클라이언트가 작업을 마치고 `DEL` 명령을 사용해 Redis에서 락 키를 삭제하면, Redis는 Pub/Sub 채널을 통해 락이 해제되었음을 모든 구독자에게 알립니다. 이를 통해 대기 중이던 클라이언트들이 락이 해제되었음을 인지하게 됩니다.

이를 통해 스핀락처럼 지속적으로 CPU를 낭비하면서 확인할 필요 없이, Redis의 상태만 확인하면 됩니다.

그렇다면 비관적 락을 사용하지 않고 분산락을 바로 사용하면 더 좋지 않을까요?

  1. 단일 데이터베이스에서 사용하는 것이라면 내부 트랜잭션 단위로 동작하기 때문에 락을 걸고 해제하는 작업이 빠르므로, 굳이 Redis라는 추가적인 작업을 할 필요는 없을 것입니다.
  2. 분산락은 Redis나 Zookeeper 같은 외부 시스템에 의존합니다. 이러한 시스템에 장애가 발생하면 신뢰성을 보장하기 위해 시스템 자체적으로 관리가 필요합니다.
  3. 비관적 락은 데드락이 발생했을 때 트랜잭션 타임아웃 설정으로 빠르게 처리할 수 있습니다. 하지만 분산락을 도입하면 클라이언트별로 타임아웃을 설정해야 하고, 해제되지 않을 경우 수동으로 해제해야 하는 경우가 발생할 수 있습니다.

항상 개발 일을 하다 보면 trade off는 발생할 수밖에 없다고 생각합니다. 어느 시스템에 적절한 방법을 활용해야 할지 알고 있다면, 차후에 발생되는 문제에 대해 적절하게 선택할 수 있다고 생각합니다.

감사합니다.

댓글

GitHub 계정으로 로그인하시면 댓글과 질문을 남기실 수 있습니다. 남겨 주신 글은 이 저장소의 Discussions에 쌓입니다.

● main 214 posts UTF-8 LF © 2026 코징 RSS · GitHub