일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- mock
- AWS
- 스프링부트
- Spring Batch
- 의존성
- JPA
- 트랜잭션
- HTTP
- AOP
- CircuitBreaker
- 우테코
- 자바
- 코드리뷰
- 레벨2
- Level2
- 스프링 부트
- yml
- Docker
- 프리코스
- 우아한테크코스
- 우아한세미나
- 세션
- Paging
- MSA
- 미션
- 프로그래머스
- JUnit5
- 서블릿
- 백준
- REDIS
- Today
- Total
늘
[Redisson] Redis RedLock의 한계와 RLock과 RFencedLock 본문
https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html
redis를 사용한 분산락을 구현할때 RedLock의 한계는 이미 잘 알려진 문제입니다. 간략히 짚고 넘어가자면 Java기반 서버에서 stw가 발생할때 또는 클락 드리프트 이슈가 발생할때 락 일관성이 깨질 수 있다는 것입니다.
이를 확실하게 해결하기 위해서는 주피터를 사용하라고 되어있습니다.
그렇다면 redis를 사용한 분산락은 어떻게 구현할 수 있을까요?
redlock알고리즘을 사용하는 이유가 클러스터 구조에서 일관성있는 락을 유지하기 위해 존재하는데, redlock을 사용하지 않는다면 어떤 식으로 redis의 java 클라이언트인 redisson이 락을 처리하는지 확인해보겠습니다.
redisson은 앞선 설명에 따라 Redlock알고리즘을 사용한 락킹을 depcrecated시켰습니다.


주석에도 설명이 있듯이 getLock메서드를 사용하라고 합니다. 그렇다면 getLock은 어떻게 되어있길래 RedLock대신 사용하라고 하는걸까요?
getLock

getLock은 RLock을 반환하며 non-fair locking입니다. 즉, 메소드가 반환하는 락은 비공정 락이므로, 쓰레드가 락을 획득하는 순서를 보장하지 않습니다. 그리고 failover 동안의 신뢰성을 높이기 위해 모든 연산은 모든 Redis 슬레이브 노드로의 전파를 기다립니다.(WAIT)
여기서 RLock 객체가 수행하는 모든 Redis 명령 실행은 Redis 3.0에 도입된 WAIT 명령을 통해 동기화됩니다. 이를통해 기존에는 master-slave 혹은 각 노드간에 비동기로 통신하면 장애 복구동안 락이 해제될 확률이 높았던 확률을 많이 낮추게 됩니다. (락이 slave 노드들에게 전파될때까지 대기하기 때문에)
하지만 확률을 낮추게 만들었을뿐 이러한 WAIT명령어도 완전한 일관성을 보장하지는 않으며 단지 최선의 선택이라고만 되어있습니다.
Java FencedLock
FencedLock은 java.util.concurrent.locks.Lock 인터페이스를 구현하는 분산 잠금입니다. FencedLock은 fencingToken (네트워크나 일시 중지된 프로세스에서 긴 지연으로부터 시스템을 보호) 을 이용하여 동작합니다.

이 토큰은 클라이언트가 잠금을 획득할 때마다 증가하는 숫자이며 클라이언트는 이 토큰을 모든 외부 서비스에 전달하고 모든 요청에 토큰을 포함해야 합니다. 서비스가 두 개의 다른 토큰으로 요청을 받은 경우 가장 높은 토큰 번호가 있는 요청을 수락하고 다른 모든 요청은 거부합니다. 이렇게 하면 두 클라이언트가 동시에 잠금을 가지고 있어도 외부 서비스는 한 클라이언트와만 상호 작용할 수 있습니다.
아래는 예시입니다.
@Test
public void testTokenIncrease() {
RFencedLock lock = redisson.getFencedLock("lock");
Long token1 = lock.lockAndGetToken();
assertThat(token1).isEqualTo(1);
lock.unlock();
assertThat(token1).isEqualTo(1);
Long token2 = lock.lockAndGetToken();
assertThat(token2).isEqualTo(2);
lock.unlock();
lock.lock();
assertThat(lock.getToken()).isEqualTo(3);
lock.unlock();
lock.lock(10, TimeUnit.SECONDS);
assertThat(lock.getToken()).isEqualTo(4);
lock.unlock();
Long token4 = lock.tryLockAndGetToken();
assertThat(token4).isEqualTo(5);
}
FencedLock은 RLock과 마찬가지로 fairness(공정성)를 제공하지 않습니다. FencedLock은 일관성과 분할 허용성을 제공하지만 가용성을 제공하지 않으므로 CAP 정리에 따르면 CP 개념이라고 합니다.
성능 차이
thread: 10 호출횟수: 1000

thread:30 호출 횟수: 1000

thread:10 호출 횟수: 10000

Reference
https://github.com/redisson/redisson/wiki/8.-distributed-locks-and-synchronizers#810-fenced-lock
https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RFencedLock.html