분산 락(Distributed Lock)은 분산 환경에서 여러 인스턴스나 프로세스가 동일한 자원에 동시에 접근하지 못하도록 동기화하는 메커니즘입니다. 분산 시스템에서 동시성 문제를 해결하는 데 특히 유용합니다. 예를 들어, 여러 인스턴스의 서버가 동시에 동일한 데이터를 업데이트하면 데이터가 불일치하거나 비정상적인 상태가 될 수 있는데, 분산 락을 통해 이러한 문제를 방지할 수 있습니다.
왜 분산 락이 필요한가요?
예를 들어, 인기 있는 게시물에 대한 좋아요 수를 집계할 때 많은 사용자가 동시에 좋아요를 누른다면, 수많은 인스턴스가 동시에 접근해 좋아요 수가 정확하지 않게 될 수 있습니다. 이때 분산 락을 적용해 하나의 인스턴스만 동시에 자원에 접근하도록 제어할 수 있습니다.
분산 락 구현 방식
분산 락은 여러 방식으로 구현할 수 있으며, 일반적으로 Redis, Zookeeper, Database를 이용한 분산 락이 널리 사용됩니다.
Redis 기반 분산 락:
Redis는 고성능 인메모리 데이터 스토어로, 짧은 TTL(Time to Live, 만료 시간)과 SETNX(Set if Not Exists) 명령어로 락을 구현할 수 있습니다.
Redisson 라이브러리(레디스 클라이언트)를 사용하면 Redis 기반 분산 락을 쉽게 적용할 수 있습니다.
Zookeeper 기반 분산 락:
Apache Zookeeper는 분산 시스템을 위한 중앙 집중형 관리 서비스입니다.
분산 락을 위한 워처(watcher)와 순차적 노드를 제공하여 자원을 점유하거나 해제하는 방식으로 락을 구현합니다.
Database 기반 분산 락:
락 테이블을 생성하고, SELECT FOR UPDATE 구문으로 락을 점유할 수 있습니다.
SQL 데이터베이스 트랜잭션이 적용되는 경우 유효하지만, 성능이 중요한 상황에는 권장되지 않습니다.
Redis를 이용한 분산 락 예제 (Redisson)
public class SomeService {
private final RedissonClient redissonClient;
public SomeService(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
public void performTaskWithLock(Long resourceId) {
String lockKey = "lock:resource:" + resourceId; // 고유한 리소스 ID를 기반으로 락 키 생성
RLock lock = redissonClient.getLock(lockKey);
try {
if (lock.tryLock(10, 2, TimeUnit.SECONDS)) { // 락을 최대 10초 동안 대기하고, 락이 걸리면 2초 동안 점유
// 작업 수행
} else {
throw new RuntimeException("락을 획득할 수 없습니다. 잠시 후 다시 시도해주세요.");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("락 획득 중 인터럽트가 발생했습니다.", e);
} finally {
lock.unlock(); // 락 해제
}
}
}
주요 개념
lock.tryLock(10, 2, TimeUnit.SECONDS): 최대 10초 동안 락을 기다리고, 락을 획득한 후 2초 동안 락을 점유하도록 설정합니다.
lock.unlock(): 작업이 끝나면 반드시 락을 해제해야 합니다.
이 방식은 Redis의 TTL을 이용해 만일의 경우 락이 풀리지 않는 상황을 방지하며, tryLock을 통해 여러 인스턴스가 락을 대기하고 동시에 자원에 접근하지 않도록 제어합니다.
Redisson의 장점
간단한 API: Redisson은 Java API를 통해 Redis 기반의 분산 락을 손쉽게 적용할 수 있습니다.
성능: Redis는 매우 빠른 성능을 제공하며, 트래픽이 높은 분산 환경에서도 분산 락을 안정적으로 사용할 수 있습니다.
내구성: Redis 클러스터나 Sentinel과 함께 사용하면 락의 신뢰성을 더욱 높일 수 있습니다.
주의사항
TTL 설정: 락을 너무 오래 점유하지 않도록 TTL(락의 만료 시간)을 설정하는 것이 중요합니다. 점유 시간이 지나면 락이 자동으로 해제됩니다.
다중 인스턴스 제어: 분산 락을 사용하더라도 높은 트래픽이 발생할 경우 시스템 성능을 주기적으로 모니터링하고 필요에 따라 락 전략을 조정해야 합니다.