2025. 4. 6. 23:33ㆍ개발
이전에 포스팅에서 트랜잭션 데드락을 여러 이유로 레디스를 활용하여 해결하였다.
예외 상황에 대해서 고민을 해보았다.
레디스 서버가 다운 된다면
만약에 레디스 서버가 다운된다면 락 기능을 활용하지 못한다. 만약에 서버를 증설할 수 있다면, 레디스 서버를 늘려서 처리하는 방법이 있다.
여러개의 레디스 서버를 어떻게 활용할까
만약 락을 관리하는 레디스 서버가 여러대라면 크게 다음과 같이 두가지 방식으로 나눌 수 있겠다.
- 모든 레디스 서버가 하나의 key에 대해 락을 공유하는 방식
- 각각의 레디스가 서로 다른 key를 관리하는 방식
1. 모든 레디스 서버가 하나의 key에 대해 락을 공유하는 방식
이 방법은 RedLock 알고리즘을 사용하는 방식이 있었으나, 다음 상황에 대해 문제가 있어 deprecated 되었다.
"GC의 stop the world 동안 키가 만료되고 다른 클라이언트가 키를 획득 후 stop the world후 앱 복구 된다
면?"
=> 하나의 시점에 두개의 클라이언트가 락을 획득하는 상황이 발생한다.
"5개의 Redis 노드 중 3개(1,2,3)에서 락을 획득한 후, 그중 1개 노드(3)가 크래시 후 재시작된다면?"
=> 다른 클라이언트에서 3개 노드(3,4,5)에서 락을 획득할 수 있어 동일한 리소스에 대해 중복 락이 발생하는 문제가 있다. (데이터가 유실되기 때문에)
=> 그러면 AOF를 사용해서 해결하자니, 모든 명령어에 대해 fsync()를 호출해야 하고 그럼 성능 저하가 발생할 것이다.
=> 3번 노드를 즉지 재시작이 아니라 TTL이후 복구 시키면 되지만 특정 노드가 특정 시간동안 비활성화, 과반수의 노드가 다운시 해결 불가라는 단점이 존재한다.
MultiLock
모든 레디스 노드에서 락을 획득해야 비로소 권한을 얻는 방법. 즉 하나의 레디스 노드에서 락을 획득하지 못하면 모든 락을 반환한다.
또 여러개의 클라이언트가 동시에 락을 획득 하려고 하면 데드락이 발생할 수도 있다.
- Redis 노드 A, B, C, D, E
- 클라이언트 1(Client-1):
- A: 락 성공
- B: 락 성공
- C: 락 성공
- D: 아직
- E: 아직
- 클라이언트 2(Client-2):
- D: 락 성공
- E: 락 성공
- A/B/C: 아직
이 방지를 위해 특정시간동안 락 획득 못하면 모든 락을 반환하고 block상태로 가서 publish를 기다린다
boolean res = multiLock.tryLock(100, 10, TimeUnit.SECONDS); // 10초동안 락 획득 못하면 반환
일관성이 매우 강하다만은 노드 하나가 죽으면 락 획득에 실패한다. 즉 이 문제의 시작점인, '하나의 노드가 실패해도 해결하도록' 이라는 목표를 완수하지 못한다.
Redisson에서는 더 많은 락을 제공하지만 결국 single node에서 보장하는 방식들이며 여러개의 노드일때 문제를 해결해 주지는 않는다.
락 정합성이 절대 깨져서는 안된다면 ZooKeeper를 사용하란다. 이 프로젝트에서는 100%의 정합성 보다는 어느정도 inconsistency는 감수하더라도 에러로 인한 사용자 경험 개선 저하가 더 중요하다. 따라서 다른 방법을 찾아봐야 한다.
(레디스 노드가 다운돼서 좋아요 기능이 안된다던지 하는 상황이 발생해서는 안된다)
2. 각각의 레디스가 서로 다른 key를 관리하는 방식(샤딩)
Zookeeper라는 인프라를 또 새로 도입하기 보다 기존의 레디스를 최대한 활용해보고 싶었다.
여러개의 레디스 노드가 서로 다른 range의 key를 관리한다면(샤딩 처럼) 레디스가 다운시, 영향을 받는 게시글의 범위는 그만큼 축소된다.
완전한 해결책은 아니지만 그 영향력을 줄일 수 있다.
예를들어 레디스 노드가 5개라면, 게시글 ID % 5 로 락을 처리할 노드를 선택하는 것이다.
그럼 3번 레디스 노드가 다운시 게시글 ID % 5 == 3 인 게시글들만 복구전까지 좋아요 기능 불가 상태일 것이지만 나머지는 정상적으로 수행이 가능하다.
레디스 클러스터링 : 샤딩
레디스에서는 hash slot이라는 샤딩을 사용한다. 총 16384개의 해시 슬롯이 있는데 이거를 여러개의 집합으로 나눠서 각 노드들이 담당하는 방식이다.
키를 해시로 변환해서 0~16386번 슬롯에 매핑하고 그 슬롯을 소유한 노드로 라우팅한다.
이렇게 함으로써 새로운 노드를 추가하거나 제거할때 리밸런싱과 같은 문제가 없어진다.
'키-> 슬롯'은 변하지 않고 '슬롯 -> 노드'만 바뀌기 때문에 새로 해시 방식을 바꿀 필요가 없어진다.
레디스 클러스터링 : 마스터 슬레이브 구조
각각의 노드에 대해 레플리케이션 노드를 두고 특정 노드 다운시 대체할 노드를 두는 방법도 있다. 하지만 이 방법은 비동기적으로 복제를 하기 때문에 완벽한 정합성을 보장하지는 않는다.
1. 클라이언트가 B에 write
2. B가 ok 응답
3. B가 B1, B2, B3에게 전파
이때 B는 B1, B2, B3에게 ok응답을 받지 않고 바로 클라이언트에게 응답을 한다. 3번이 진행되기 전에 B가 다운되면 write한 데이터가 유실된다.
이를 해결하기 위해 모든 AOF에 대해 fsync()로, 즉 디스크에 flush하는 방법이 있으나 이는 성능 저하로 연결된다.
레디스는 wait 명령어를 통해 동기적 write를 지원한다. 우리 서비스에서 이미 저장된 게시글에 대해 write의 발생률은 적다고 가정하였으니 이 방법이 적합할 것이다.
레디스 클러스터링 : 네트워크 파티션
네트워크 파티션의 경우에도 클라이언트의 write가 유실될 수 있다.
3개의 마스터와 3개의 레플리카 A, B, C, A1, B1, C1 로 클러스터가 구성되어있을 때, (A,B,C1), (A1,B,C) 로 네트워크 분할이 일어난다면,
각 파티션의 레플리카는 failover로 write을 수행할 것이다.
이때 같은 key에 대해 양쪽 파티션에서 write이 일어나고 이로써 서로 다른 데이터를 갖고 있게 된다면 파티션 제거후 다시 합칠 때, 정합성 문제가 발생한다.
해결책 : 클러스터에 항상 홀수개의 샤드를 유지하기
항상 홀수개의 샤드를 유지한다면 분할이 일어나도 결국 한쪽은 다른 한쪽보다 더 많은 샤드를 유지할 것이다.
이점을 이용하여 수가 많은 파티션에 속한다면 failover를 수행하고 write을 허용,
수가 적은 파티션에 속한다면 failover를 수행하지 않고 write또한 허용하지 않는 방법으로 위의 정합성 문제를 해결할 수 있다.
다만 억지로 홀수개를 맞추기 위해 추가 인프라를 가용해야 한다는 점이 우려된다
다음글
https://loftspace.tistory.com/51
Redis의 응답이 느릴때
현재 서비스에서, 레디스에는 많은 종류의 데이터가 저장되어있다.각 게시글에 대한 락정보인기 게시글 정보유저-좋아요 정보게시글의 좋아요 카운팅하나의 레디스 서버에서 4개의 역할을 수
loftspace.tistory.com
Reference :
https://redis.io/docs/latest/develop/use/patterns/distributed-locks/
Distributed Locks with Redis
A distributed lock pattern with Redis
redis.io
https://redisson.pro/docs/data-and-services/locks-and-synchronizers/?utm_source=chatgpt.com#redlock
Distributed locks and synchronizers - Redisson Reference Guide
Distributed locks and synchronizers Lock Redis or Valkey based distributed reentrant Lock object for Java and implements Lock interface. Uses pub/sub channel to notify other threads across all Redisson instances waiting to acquire a lock. If Redisson insta
redisson.pro
https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html
How to do distributed locking — Martin Kleppmann’s blog
How to do distributed locking Published by Martin Kleppmann on 08 Feb 2016. As part of the research for my book, I came across an algorithm called Redlock on the Redis website. The algorithm claims to implement fault-tolerant distributed locks (or rather,
martin.kleppmann.com
'개발' 카테고리의 다른 글
Redis의 응답이 느릴때 (메모리 사용량) (0) | 2025.04.30 |
---|---|
인기게시글 도입을 위한 과정 - 추가적인 고민 (0) | 2025.04.25 |
트랜잭션 데드락 - 외래키 제약조건 (0) | 2025.04.06 |
인기 게시글 도입을 위한 과정 - 자료구조의 선택 (0) | 2025.04.06 |
인기 게시글 도입을 위한 과정 - 캐싱과 쓰기전략 (1) | 2025.04.06 |