https://redis.io/docs/latest/develop/use/patterns/distributed-locks/
리소스에 락을 하는 가장 간단한 방법은 인스턴스에 키를 만드는 것이다. 키는 대개 제한된 시간동안에 살아있고, 레디스 만료 기능을 통해서 해제된다. 클라이언트가 리소스를 해제하면, 키는 삭제된다. 그렇지만 여기에는 문제가 있다. 마스터 노드가 죽는다면, 단일 실패 지점이 될 수 있다. 그렇다고 레플리카를 추가할 순 없다. 레디스 복제는 비동기적으로 이뤄지므로 Safety property
를 위배할 수 있다.
위배하는 상황은 아래와 같다.
Safety property
를 위배한다.만약 락을 동시에 갖는 것이 문제가 되지 않는다면 이 모델을 사용할 수 있다.
SET resource_name my_random_value NX PX 30000
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
N개의 레디스 마스터가 있다고 가정한다. 노드들은 모두 독립적이고, 레플리카를 사용하지 않았다. 위에서 싱글 인스턴스에 대해서 안전하게 락을 획득하고 해제하는 방법을 알고 있다. 5개 레디스 마스터를 독립적인 환경에서 실패할 수 있도록 각자 다른 가상 환경에서 수행시킨다.
락을 얻기 위해서 클라이언트는 아래 작업들은 수행한다.
N개의 레디스를 통해서 고가용성 및 일관성 확보
N개의 노드가 있으면, 노드의 환경에 따라서 만료 시간을 통한 시간 동기화 문제가 발생할 수 있다. 노드마다 락을 얻기 위해 경과된 시간이 다르면, 해제 시간이 만료될 때 문제가 발생할 수 있다.
과반수를 통한 락 획득을 통해서 안정성을 유지할 수 있도록 한다.
클라이언트가 락을 획득하지 못하면, 랜덤한 딜레이 후에 재시도를 하게 된다. 대다수가 락을 획득하게 되면, 더 이상 나머지에 대해서는 재시도 하지 않아도 된다. 또한 잠금을 획득하지 못한 경우, 일부 획득한 잠금을 최대한 빨리 해제하는 것이 중요하다. 이렇게 빨리 잠금을 해제하면 키 만료를 기다릴 필요가 없어진다.
특정 인스턴스에 락을 획득하거나 획득하지 못했더라도 락을 해제하는 것은 간단하다.
여러 개의 인스턴스에 락을 획득하려고 하면, 키는 다양한 시간대에 셋팅 될 수 있다. 만약에 T1을 가장 처음 락을 획득한 시간, T2를 가장 나중에 락을 획득한 시간으로 하면 아래의 식이 만족된다.
MIN_VALIDITY=TTL-(T2-T1)-CLOCK_DRIFT
최소 유효 기간동안에는 동시에 락을 잡을 수 없음을 의미한다.
이미 N/2+1
노드가 락되어 있는 동안에, 다른 클라이언트가 N/2+1
을 획득할 수 없다.
최대 만료 시간(MAX_VALIDITY
)보다 크거나 가깝게 락을 획득한 클라이언트는 락은 무효화되고 인스턴스들에서 락을 해제한다. 그러므로, 만료 시간내에 락을 획득한 경우에만 동시에 락을 잡지 못하는지 고려하면 된다. 이 상황에 대해서는 이미 MIN_VALIDITY
와 N/2+1
로 고려되어 있다.
네트워크가 분할되면 TTL 시간만큼의 가용성 저하가 발생하며, 만약 네트워크 분할이 지속적으로 발생하면 이 가용성 저하는 무한히 계속될 수 있다. 이런 상황은 클라이언트가 락을 획득한 후, 락을 해제하기 전에 네트워크가 분할되어 해당 인스턴스와 연결이 끊겼을 때 발생한다.
TBD