Skip to content

Redis에서 사용하는 Distributed Lock에 대해서 설명해주세요

Redis에서 Distributed Lock(분산 잠금) 은 여러 인스턴스에서 동시에 접근할 수 있는 자원에 대한 경쟁을 조정하기 위한 방법으로 사용됩니다. 분산 잠금은 특히 분산 시스템에서 여러 노드가 동일한 자원을 동시에 수정하려고 할 때, 데이터의 무결성과 일관성을 보장하는 데 필요합니다.

Redis는 기본적으로 SETNX (Set if Not Exists)와 EXPIRE 명령어를 활용하여 분산 잠금을 구현할 수 있으며, 여기서 가장 널리 사용되는 알고리즘은 Redlock 알고리즘입니다.

Redis에서 분산 잠금의 원리#

Redis를 사용한 분산 잠금은 보통 아래의 단계를 거쳐 구현됩니다:

1. SETNX (Set if Not Exists)#

  • SETNX 명령어는 지정된 키가 없을 때만 값을 설정합니다. 즉, 특정 자원에 대한 잠금을 시도할 때 해당 자원에 아직 잠금이 걸려있지 않으면 성공적으로 잠금을 걸 수 있습니다.
  • 예: SETNX lock_key "locked"

2. EXPIRE (만료 시간 설정)#

  • SETNX로 잠금을 설정한 후, EXPIRE 명령어를 사용하여 이 잠금에 유효 기간(만료 시간)을 설정합니다. 이는 잠금이 영구적으로 유지되는 것을 방지하고, 비정상적인 상황(예: 프로세스가 중단됨)에서 자원이 영구적으로 잠기는 것을 방지합니다.
  • 예: EXPIRE lock_key 10 (10초 동안 잠금 유지)

3. GETSET (조건부 갱신)#

  • 자원을 안전하게 잠금 해제하거나 잠금을 연장할 때 GETSET 명령어를 사용할 수 있습니다. 이 명령어는 키의 이전 값을 반환하면서 새로운 값을 설정해주므로, 잠금의 유효성을 체크하고 갱신하는 데 사용됩니다.

Redlock 알고리즘 (Redis에서 권장하는 분산 잠금)#

Redis에서 분산 잠금을 안전하게 관리하기 위한 알고리즘으로 Redlock이 제안되었습니다. 이 알고리즘은 여러 Redis 노드에서 동시에 잠금을 획득하고, 시스템 장애나 네트워크 지연에도 안전하게 잠금을 관리하기 위한 방법입니다.

Redlock의 기본 원리#

  1. 잠금 생성: 클라이언트는 여러 Redis 인스턴스에 동일한 키(잠금 키)를 동시에 요청하여 잠금을 설정합니다. SETNX 명령어를 사용하여 성공적으로 잠금을 설정한 인스턴스만 기록됩니다.
  2. 만료 시간 설정: 각 인스턴스의 잠금에는 고유한 만료 시간이 설정되며, 만료 시간이 지나면 자동으로 잠금이 해제됩니다.
  3. 잠금 획득 성공 여부: 클라이언트는 특정 다수(보통 과반수 이상)의 Redis 인스턴스에서 잠금에 성공해야만 자원에 대한 권한을 얻었다고 간주합니다.
  4. 잠금 해제: 작업이 완료되면 클라이언트는 각 Redis 인스턴스에서 잠금을 해제합니다. 잠금은 클라이언트가 직접 해제하거나, 설정된 만료 시간이 지나면 자동으로 해제됩니다.

Redlock의 장점#

  • 안전성: 분산 시스템에서 하나의 Redis 노드에 문제가 생겨도 다른 노드에서 안전하게 잠금을 관리할 수 있습니다.
  • 복구성: 네트워크 장애가 발생해도 잠금이 유효 기간 동안 유지되고, 만료 후 자동으로 해제됩니다.
  • 고가용성: 여러 Redis 노드에서 과반수의 인스턴스에서 잠금이 유지되므로, 한 노드가 다운되더라도 시스템은 여전히 동작할 수 있습니다.

Redlock 예제 코드#

import time
import redis

# Redis 클라이언트 설정
client = redis.StrictRedis()

# 잠금을 설정할 키
lock_key = "resource_lock"
lock_timeout = 10  # 잠금 유효 기간 10초

# 1. SETNX를 사용하여 잠금 시도
is_locked = client.set(lock_key, "locked", nx=True, ex=lock_timeout)

if is_locked:
    try:
        # 잠금 획득 성공: 자원에 대한 안전한 작업 수행
        print("잠금을 획득했습니다. 안전하게 작업을 수행합니다.")
        time.sleep(5)  # 작업 수행 중
    finally:
        # 2. 작업이 완료되면 잠금 해제
        client.delete(lock_key)
        print("잠금이 해제되었습니다.")
else:
    print("잠금을 획득하지 못했습니다. 다른 클라이언트가 잠금을 보유 중입니다.")

Redis 분산 잠금 사용 시 주의사항#

  1. 잠금 해제 실패 문제: 클라이언트가 예기치 않게 종료되거나 네트워크 문제가 생기면 잠금이 해제되지 않을 수 있습니다. 이를 방지하기 위해 적절한 만료 시간을 설정하고, 잠금을 해제할 때는 GETSET 등을 사용해 만료 시간의 유효성을 검사해야 합니다.

  2. TTL(만료 시간) 설정: 잠금 유효 기간은 너무 짧거나 길면 문제가 될 수 있습니다. 작업 시간이 긴 경우, 충분한 유효 기간을 설정하는 것이 중요합니다.

  3. 시계 동기화: Redlock 알고리즘을 사용할 때 Redis 노드 간의 시계 동기화가 중요합니다. 노드 간 시간이 일치하지 않으면 잠금 해제 타이밍이 어긋나 문제를 일으킬 수 있습니다.

Redis를 사용한 분산 잠금은 특히 분산 환경에서 자원의 동시성 제어를 위한 강력한 도구입니다. SETNX, EXPIRE 등을 적절히 사용하고, 필요에 따라 Redlock 알고리즘을 적용하면 안전하게 분산 잠금을 구현할 수 있습니다.