Lined Notebook

🔒 동기화를 보장하기 위한 분산 락

by juraffe juraffe

최근 포트폴리오 프로젝트에 클러스터링을 적용하면서 문제가 발생하였다. 현재 구현된 동작 중에서 등록된 서버를 일정한 주기로 접속자를 기록하는 잡 스케줄이 등록되어 있는데, 이 녀석이 클러스터라이징된 만큼 동시에 수행되어 중복된 데이터를 만드는 것이다.

 

이 부분을 MSA 같은 느낌으로 따로 빼서 구현하면 좋지 않을까?라는 생각을 했지만 다양한 방법을 경험해보기 위해서 분산 락을 활용해 문제를 해결해봤다. (이런 문제는 MSA로 빼서 만드는게 현명하다고 생각이 들지만 ^^...)

 

분산 락은 앞서 설명한 바와 같이 서버가 여러 대인 상황에서 동일한 데이터(예를 들어 DB)에 대한 동기화를 보장하기 위해 사용된다. 단순히 하나의 서버의 경우 node.js는 모든 코드 블록이 동기화되어 수행되지만 이 과정도 서버가 분산된 환경에서는 데이터의 동기화를 보장할 수 없다. 이때 동기화를 목적으로 임계점에 Lock(이하 락)을 거는 것이 분산 락이라 할 수 있다.

 

분산 락을 구현하기 위해선 락을 표시하기 위해 물리적으로 떨어져 있거나 다른 프로세스와 공유할 수 있는 데이터 저장 공간이 필요하며, 동시에 락을 거는 경우를 막기 위해서 Atomic 한 연산이 필요하다.

 

안전한 분산 락을 위해 Redis(이하 레디스)에서는 RedLock이라는 알고리즘을 제안하고 있으며 3가지의 특성을 보장해야 한다고 말한다.

  • 오직 한 순간에 하나의 작업자만이 락을 걸 수 있다.
  • 락 이후, 어떠한 문제로 인해 락을 풀지 못하고 종료된 경우라도 다른 작업자가 락을 획득할 수 있어야 한다.
  • 레디스 노드가 작동하는 한 모든 작업자는 락을 걸고, 해체할 수 있어야 한다.

현재 많은 레디스 기반 분산 락 라이브러리는 키 생성하며 구현하여 사용된다. 키를 생성하게 되면 ttl동안 존재하며 사라지는 특성과 레디스 자체의 특성을 이용해 위에서 제안된 특성을 만족한다고 생각될 수 있다. 하지만 만약 레디스 자체에서 키를 등록하기 전 충돌이 발생하고 이를 클라이언트 A 측으로 전달하기 전에 클라이언트 A가 임계 영역에 들어가고, 또 다른 클라이언트 B가 키를 등록하고(이전 클라이언트 A가 요청한 락은 충돌로 인해 락이 걸리지 않아, 클라이언트 B는 락이 걸려있다고 판단할 수 없다.) 임계 영역으로 들어가는 문제가 발생할 수 있다.

 

분산 알고리즘에 들어가기 앞서 단일 인스턴스에서 안전하게 동작하기 위한 몇가지 기초를 알아보자,

// SET
SET resource_name my_random_value NX PX 30000

// DEL
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

레디스에서 락을 설정할 때 랜덤 한 값을 가지고 저장하게 된다. 이러면 이후 다른 클라이언트가 락을 제거할 때 키뿐만 아니라 고유한 식별자를 가지는 값을 비교해서 다른 클라이언트가 설정한 락을 제거하는 것을 예방할 수 있다.

 

이 아이디어를 가지고 Redlock 알고리즘은 다음과 같은 과정을 수행한다.

  1. 현재 시간(ms)을 가져온다.
  2. 모든 인스턴스에 동일한 키와, 랜덤한 값을 사용하여 순차적으로 잠금을 시도한다. 이때, 다운된 Redis 노드와의 대화를 위해 지연되는 걸 막기 위해서 짧은 시간을 제한으로 두고 빠르게 다음 Redis 노드로 넘어간다.
  3. (현재 시간 - 1단계에서 얻은 시간)을 통해서 경과 시간을 구하고 경과 시간이 잠금 유효 시간보다 작으면 잠금을 획득했다고 간주한다.
  4. 잠금을 획득한 경우 유효 시간은 3단계에서 계산 된대로 초기 유효 시간 - 경과 시간으로 간주한다.
  5. 잠금에 실패한 경우 (유효 시간이 음수이거나, N/2+1개의 인스턴스에서 잠금에 실패한 경우) 모든 인스턴스에서 잠금을 해제한다.

참고

 

'🌐 프로그래밍' 카테고리의 다른 글

🔒 동기화를 보장하기 위한 분산 락  (0) 2021.05.07
문자열 최적화  (0) 2019.10.25
std::c++  (0) 2019.06.23

블로그의 정보

🦒 Juraffe's note

juraffe juraffe

활동하기