티스토리 뷰

분산락을 사용할 때 락 해제 실패(lock release failure) 문제는 큰 위험 요소가 될 수 있습니다. 만약 락이 정상적으로 해제되지 않으면 데드락(Deadlock) 이 발생하거나 리소스가 불필요하게 점유되는 문제가 생길 수 있습니다. 아래에서 주요 원인과 해결책을 설명드릴게요.

 

 

락 해제 실패의 주요 원인

애플리케이션 비정상 종료

  • 락을 획득한 애플리케이션이 예상치 못한 종료 (Crash, OOM, Kill Signal) 가 발생하면 락이 유지됩니다.
  • 락을 잡은 프로세스가 락을 해제하지 못하고 종료될 경우, 다른 프로세스가 락을 기다리면서 데드락이 발생할 수 있습니다.

 

TTL(Time-To-Live) 설정 미비

  • Redis에서 SETNX로 락을 설정한 후 TTL(EXPIRE)을 설정하지 않으면, 락이 영구적으로 유지될 가능성이 있습니다.
  • 락을 해제하는 로직이 정상적으로 실행되지 않으면, 락이 자동으로 풀리지 않습니다.

 

락 해제 시 잘못된 키 삭제

  • 락을 획득한 클라이언트가 다른 클라이언트의 락을 해제하는 경우입니다.
  • 예를 들어, 락 키(lock_key)가 만료되었고 다른 프로세스가 같은 키를 다시 생성한 경우, 기존 프로세스가 락 해제 요청을 보냈을 때 의도치 않게 다른 프로세스의 락을 삭제할 가능성이 있습니다.

 

네트워크 지연 및 분산 환경 이슈

  • 락을 획득한 노드에서 락 해제 요청을 보냈지만, 네트워크 이슈로 인해 요청이 늦게 도착하는 경우, 이미 락이 만료되었거나 다른 노드에서 새로운 락이 설정될 수 있습니다.
  • 이 경우, 원래 락을 획득한 프로세스가 늦게 도착한 해제 요청으로 다른 프로세스의 락을 해제해버리는 문제가 발생할 수 있습니다.

 

 

 

해결책

TTL(Time-To-Live) 설정 필수

  • 락을 획득할 때 반드시 TTL을 설정하여 일정 시간이 지나면 자동으로 해제되도록 만들어야 합니다.
  • Redis에서는 SET key value NX PX timeout 명령을 사용하면 원자적으로 TTL을 설정할 수 있습니다.
SET lock_key "unique_value" NX PX 5000
  • NX: 키가 없을 때만 설정
  • PX 5000: 5초 후 자동 만료

 

락 해제 시 확인 절차 추가 (락 소유 검증)

  • 락을 해제할 때, 현재 락이 자신이 설정한 락인지 확인한 후 해제해야 합니다.
  • Redis에서는 Lua 스크립트를 활용하여 이를 원자적으로 수행할 수 있습니다.
if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("del", KEYS[1])
else
    return 0
end
  • 위 스크립트는 락을 획득한 클라이언트가 설정한 값(ARGV[1])이 현재 락의 값과 같을 때만 락을 삭제합니다.

 

Java 코드 예제는 다음과 같습니다. (Redisson 없이 구현)

public boolean releaseLock(Jedis jedis, String lockKey, String uniqueValue) {
    String luaScript =
        "if redis.call('get', KEYS[1]) == ARGV[1] then " +
        "   return redis.call('del', KEYS[1]) " +
        "else " +
        "   return 0 " +
        "end";
    Object result = jedis.eval(luaScript, Collections.singletonList(lockKey),
                               Collections.singletonList(uniqueValue));
    return "1".equals(result.toString());
}

 

 

Redlock 알고리즘 적용 (다중 노드 환경에서 신뢰성 보장)

  • Redis를 단일 노드에서 사용하면 장애 발생 시 락 정보가 유실될 수 있기 때문에 Redlock 알고리즘을 적용하면 더 안전한 락 처리가 가능합니다.
  • Redlock은 여러 개의 Redis 노드(3개 이상) 에 락을 동시에 설정하고, 과반수 이상의 노드에서 성공했을 때만 락을 획득하는 방식입니다.
  • Redisson에서는 이미 이를 지원하고 있습니다.

 

Redisson을 활용한 분산락 구현 예제는 다음과 같습니다.(Spring Boot)

@Autowired
private RedissonClient redissonClient;

public void processWithLock() {
    RLock lock = redissonClient.getLock("myLock");

    try {
        if (lock.tryLock(10, 5, TimeUnit.SECONDS)) { // 10초 동안 시도, 5초 유지
            try {
                // 락이 획득된 경우 실행할 비즈니스 로직
                System.out.println("락 획득 성공");
            } finally {
                lock.unlock();
            }
        } else {
            System.out.println("락 획득 실패");
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
}
  • tryLock() 을 사용하면 락 획득을 시도하는 최대 시간과 락 유지 시간을 설정할 수 있어 비정상 종료 시에도 락이 일정 시간 후 자동 해제됩니다.

 

락 만료 시간 연장 (Watchdog 방식 적용)

  • 락을 오래 유지해야 하는 경우 Watchdog 방식으로 TTL을 자동 연장하는 방법이 있습니다.
  • Redisson에서는 lock.lock()을 사용하면 자동으로 TTL을 연장해주는 기능을 제공합니다.
RLock lock = redissonClient.getLock("myLock");
lock.lock(); // 기본적으로 30초 유지하며, 작업이 계속 진행되면 자동 연장됨.

 

  • Redisson의 Watchdog 기능 덕분에 프로세스가 종료되지 않는 한 락이 해제되지 않고 유지됩니다.
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/03   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
글 보관함