프로젝트/redis

분산락을 위한 Redis RedLock 톺아보기

Junuuu 2024. 7. 20. 20:42
728x90

분산락이란?

분산락은 여러 개의 분산된 서버가 존재할 때 동일한 데이터에 대한 동기화를 보장하기 위해 사용하는 락의 종류 중 하나입니다. 

 

분산락을 활용하면 서로 다른 서버들이 critical section 실행을 동시에 하지 못하도록 방지할 수 있습니다.

 

이때 MySQL의 네임드락, Zookeeper 등을 활용해서 분산락을 구현할 수 있는데 Redis의 RedLock 알고리즘을 활용하여 분산락을 구현할 수 있습니다.

 

Redis가 다운되는 경우?

단일 인스턴스를 활용하여 분산락을 구현할 수 있지만 이는 단일 실패점이 될 수 있습니다.

 

단일 실패점을 극복하기 위해서는 Master - Slave구조를 선택할 수 있습니다.

 

하지만 Slave로의 복제는 비동기이므로 아래와 같은 상황에서 race condition이 발생할 수 있습니다.

 

1. Client A가 Master로 Lock을 획득한다.
2. Master의 Lock 획득이 Slave로 복제되기 전에 Master가 다운된다.
3. Slave가 Master로 승격된다.
4. 새로운 Master에는 Lock이 구성되지 않았으므로 Client B가 Lock을 획득할 수 있게 됩니다.

 

Master - Slave에서 race condition을 방지하기 위해서 RedLock 알고리즘을 활용해서 해결할 수 있습니다.

 

N개의 독립적인 마스터 노드들을 이용하여 과반수 이상의 노드에서 Lock을 획득하면 분산락을 획득한 것으로 판단합니다.

 

예를 들어 3대의 마스터노드가 존재하면 2대에서 Lock을 획득하면 분산락을 획득한 것으로 판단합니다.

 

예를 들어 5대의 마스터노드가 존재하면 3대에서 Lock을 획득하면 분산락을 획득한것으로 판단합니다.

 

1대, 2대의 마스터 노드에 문제가 생기더라도 문제가 없기 때문에 더 안전하게 락 획득을 제어할 수 있습니다.

 

RedLock 알고리즘

1. 현재 시간을 ms 단위로 가져옵니다. (락 획득을 요청할 때 짧은 타임아웃을 두어 다운된 Redis 노드와 오랫동안 통신하는 경우를 방지합니다)
2. 클라이언트는 모든 N개의 Master에게 락 획득을 시도합니다. (동시에 setnx 요청)
3. 과반수 (N/2 +1) 인스턴스에서 락을 획득했다면 성공적으로 획득한 것처럼 간주합니다.
4. 획득에 실패했다면 N대의 노드에 해제 요청을 보냅니다.
5. 이후 재시도 정책에 따라 락 획득 재시도를 수행하거나 끝냅니다.

 

 

주의해야하는 부분 1 - 락 해제에 대한 검증

락을 획득한 클라이언트가 점유를 해제할 수 있도록 해야 합니다.

 

그렇지 않으면 A 클라이언트가 락을 점유했지만 B 클라이언트가 해제 요청으로 락을 해제한다면 race condition 문제가 발생할 수 있습니다.

 

구현방법으로는 Unique한 ID인 UUID를 setNx의 value로 활용하여 락을 점유하고 해제할때 검증해볼 수 있습니다.

 

주의해야하는 부분 2 - split brain condition

 

3대의 Redis Master가 존재하고 Client A, B, C가 각각 Master에서 잠금을 획득하는 경우 어떤 클라이언트도 잠금을 획득하지 못하는 상황이 발생할 수 있습니다.

 

가능한 동시에 여러 인스턴스에 SET 명령을 보내어 락 획득 과정의 시간을 최소화하여 가능성을 줄일 수 있습니다.

 

 

주의해야하는 부분 3 - 애플리케이션 단에서의 중단 혹은 네트워크 지연 문제가 발생하면 두 클라이언트가 락을 획득할 수 있다.

 

보통 락을 획득하고 나서 특정 클라이언트가 영원히 락을 점유하는 것을 막기 위해 데드락을 방지하기 위해서 5분, 10분등의 ttl을 두고 락을 획득합니다.

 

이때 락을 획득하고나서 애플리케이션이 GC 등으로 인하여 오랫동안 중지되거나 네트워크 지연문제등이 발생하는 경우에는 락의 유효기간이 만료되어 다른 클라이언트에서 획득하게 됩니다.

 

주의해야하는 부분 4 - Clock Drift로 인해 두 클라이언트가 락을 획득할 수 있다. .

Redlock 알고리즘은 노드들 간에 동기화된 시계(synchronized clock)는 없지만, 로컬 시간이 거의 동일한 속도로 갱신된다는 가정에 의존합니다.

 

하지만 현실에서는 클럭이 정확한 속도로 동작하지 않는 클럭 드리프트(Clock Drift) 현상으로 시스템 내부 시계가 실제 시간과 어긋날 수 있습니다.

 

예를 들어 시스템에 Redis 노드 5개(A, B, C, D, E)와 클라이언트 2개(1, 2)가 존재할 때 클럭 드리프트 현상으로 분산락 알고리즘은 깨질 수 있습니다.

 

1. 클라이언트 1이 노드 A, B, C에서 잠금을 획득하지만, 네트워크 문제로 인해 D와 E에서는 잠금 획득에 실패한다.
2. 이때 노드 C의 시계가 앞으로 이동하여 잠금이 만료된다.
3. 클라이언트 2가 노드 C, D, E에서 잠금을 획득하지만, 네트워크 문제로 인해 A와 B에서는 잠금 획득에 실패한다.
4. 이제 클라이언트 1과 2는 모두 자신이 잠금을 획득했다고 믿는다.

 

마무리

Redis의 RedLock 알고리즘을 활용하면 고가용성을 갖춘 Lock 획득 구조를 마련할 수 있습니다.

 

하지만 다양한 이유로 인해 race condition이 발생할 수 있고 분산락과 더불어 추가적인 낙관적락을 통해서 이런 한계점을 극복해 볼 수 있습니다.

 

 

참고자료

https://redis.io/docs/manual/patterns/distributed-locks/

https://junuuu.tistory.com/877

https://velog.io/@ohjinseo/Redis-%EB%B6%84%EC%82%B0%EB%9D%BD-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-Redlock

https://mangkyu.tistory.com/311

https://channel.io/ko/blog/distributedlock_2022_backend