분산 락
여러 대의 서버나 시스템에서 동시에 실행되는 프로세스/스레드가 접근하는 공유 자원에 대해 제어하기 위한 락이다
분산 시스템에서는 여러 개의 노드가 동일한 자원에 동시에 접근할 수 있기 때문에 락을 통해 공유 자원을 동기화한다
동작 방식
주로 락을 관리하는 클러스터 환경(중앙 서버나 스토리지 시스템)에서 분산 락을 구현한다
여러 노드 간에 락이 공유되며 다음과 같은 방식으로 동작한다
락 획득
프로세스/스레드가 자원에 접근하기 위해 락을 획득한다
락을 얻은 노드만 해당 자원에 접근할 수 있으며 이미 락이 사용 중이라면 대기한다
자원 접근
락을 얻은 노드가 자원에 접근한다
락 해제
작업이 끝나면 락을 해제하야 다른 노드가 자원에 접근할 수 있도록 한다
요구사항
원자성
- 원자적으로 락을 설정하고 해제하는 작업이 이뤄져야 한다
- 락을 설정하는 동안 다른 노드가 락을 제어(설정/해제)하지 못하게 해야 한다
타임아웃
- 락에 타임아웃을 설정하여 일정 시간 후 자동으로 해제되도록 해야 한다
- 락을 설정한 노드가 예기치 않게 종료되었을 경우, 락이 해제되지 않고 다른 노드들이 계속 대기할 수 있다
자동 해제
- 락을 설정한 노드가 종료되면 락이 자동으로 해제되어야 한다
- 이를 통해 시스템 부하를 줄이고 다른 노드가 락을 획득할 수 있도록 한다
redis 구현
lettuce 스핀 락 부하 문제
redis lettuce 클라이언트를 사용하여 분산 락을 구현하면 SETNX
명령 또는 lua 스크립트를 호출하여 락을 얻을 때 까지 반복적으로 락 획득을 시도한다
이런 방식으로 분산 락을 구현하게 되면 레디스 서버가 받는 부하가 커지게 된다
- 스핀 락: 락을 획득할 때까지 반복적으로 폴링을 수행하는 방식
스핀 락 부하 문제 완화 방법
1. 지수 백오프(exponential backoff) 또는 고정 대기 시간
락 획득 시도 사이에 대기 시간을 두는 방법이다
일정 시간 대기 후 다시 시도하거나, 대기 시간을 점점 늘리는 지수 백오프 전략을 사용한다
while(!tryLock(redisKey)) {
Thread.sleep(100);
}
2. 락 시도 제한
무한 반복 대신 최대 시도 횟수를 제한하고 일정 횟수 이상 실패하면 락 획득을 포기하도록 구현한다
3. redis lua 스크립트 사용
레디스는 lua 스크립트를 통해 복잡한 작업을 원자적으로 실행할 수 있는 기능을 제공한다
lua 스크립트는 레디스 서버에서 실행되며 여러 명령을 하나의 작업으로 처리하여 성능과 일관성을 높인다
4. redisson 사용
lettuce 대신 레디스 기반의 분산 락 구현 라이브러리인 redisson을 사용한다
redisson은 기본적으로 락 재시도와 대기 시간을 관리해주는 메커니즘을 제공한다