caffeine-library / system-design-interview

🌱 가상 면접 사례로 배우는 대규모 시스템 설계 기초를 읽는 스터디
4 stars 0 forks source link

[additional] 레이스 컨디션 극복하기 #15

Closed ngwoon closed 2 years ago

ngwoon commented 2 years ago

연관 챕터

10

조사 내용

이 주제가 등장하기까지의 책의 흐름

“분산 환경에서의 처리율 제한 장치” 라는 주제에서, 레이스 컨디션과 동기화 문제가 제시되었다.

동기화 문제에 대한 방안으로 중앙 집중형 캐시 서버가 제안되었고, 이렇게 여러 클라이언트(여기서는 처리율 제한 장치)가 공유하는 데이터는 레이스 컨디션을 고려하지 않을 수 없다.

단일 머신에서 이용할 수 있는 세마포어, 뮤텍스와 같은 Lock부터 시작해서, 레디스에서는 어떤 방식으로 레이스 컨디션을 극복하는지, 분산 환경에서의 레이스 컨디션은 어떻게 극복하는지에 대해 살펴보았다.

락 (Lock)


세마포어 vs 뮤텍스

https://prepinsta.com/wp-content/uploads/2019/01/Mutex-Vs-Semaphore-1-1024x429.png

출처 : https://prepinsta.com/operating-systems/mutex-vs-semaphore/

세마포어는 파일시스템 내 어딘가에 보관 & 이용되며, 여러 프로세스에서 공유되는 열쇠

뮤텍스는 프로그램 실행 시 해당 프로그램 내에서만 공유되는 열쇠

세마포어는 하나의 크리티컬 섹션에 여러 쓰레드 혹은 프로세스가 들어갈 수 있다.

뮤텍스는 하나의 크리티컬 섹션에 단 하나의 쓰레드만 들어갈 수 있다.

세마포어는 보통 양의 정수값을 갖는 변수를 컨트롤하는 방식이다. 즉, 이 변수의 초기값을 1로 설정해 두면, 뮤텍스와 동일한 기능을 수행한다.

뮤텍스는 크리티컬 섹션에 들어가는 쓰레드가 뮤텍스 객체의 “소유권”을 갖고, 이 소유권이 없는 쓰레드는 크리티컬 섹션에 들어갈 수 없다. 즉, 뮤텍스는 세마포어처럼 동작할 수 없다.

Optimistic Lock vs Pessimistic Lock

https://enterprisecraftsmanship.com/images/2017/2017-09-18-1.png

출처 : https://enterprisecraftsmanship.com/posts/optimistic-locking-automatic-retry/

어떤 공유 리소스에 대해 lock을 거는 매커니즘은 크게 optimistic, pessimistic으로 나뉜다.

Redis - 정렬 집합 (Sorted Set)


https://d194zelh06zukz.cloudfront.net/2020/03/redis-sorted-set-2.png

출처 : https://jupiny.com/2020/03/28/redis-sorted-set/

Sorted Set 혹은 ZSet이라고 부른다. (이하 zset)

zset은 key-value형 자료구조이며, 하나의 key에 member, score쌍들이 저장되는 구조이다.

보통 set 자료구조는 정렬이나 삽입 순서를 보장하지 않지만, zset 자료구조는 score기반 정렬을 보장하는 자료구조이다. (score는 부동 소수점 자료형만 가능)

zset은 삽입, 삭제는 O(1), 검색은 O(logN) 시간복잡도를 갖는다.

검색시간이 O(logN)이 될 수 있는 이유는, zset이 두 가지 자료구조를 활용해 표현되기 때문이다.

Redis의 레이스 컨디션 극복


WATCH

Redis에서의 레이스 컨디션은 보통 여러 클라이언트가 각자의 트랜잭션을 진행중일 때 발생한다.

Redis는 WATCH 명령을 통해 zset 특정 key의 변경 여부를 감지할 수 있다.

트랜잭션을 커밋하기 전 (EXEC명령 전), WATCH해둔 key의 member 혹은 score에 변경이 감지되면 해당 트랜잭션을 수행하지 않는다.

(= Optimistic Lock)

https://media.vlpt.us/images/hyeondev/post/6fafd94a-3b9e-4727-959a-4af2ea5cde60/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-03%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.49.40.png

출처 : https://media.vlpt.us/images/hyeondev/post/6fafd94a-3b9e-4727-959a-4af2ea5cde60/스크린샷 2020-10-03 오전 1.49.40.png

루아 스크립트

Redis에서 Pessimistic Lock을 구현하기 위해서는

위 두 가지 연산이 atomic해야한다.

Redis의 SETNX 명령어를 이용해 위 두 가지 연산을 atomic하게 수행할 수 있다.

하지만 이 방법을 사용하면 polling 기법으로 Lock의 사용 가능 여부를 확인해야 한다.

Redisson에서는 pub/sub 기법과 트랜잭션, 루아 스크립트를 활용해 레디스를 Pessimistic Lock으로써 사용할 수 있게끔 한다.

// in RedissonLock.java
<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
    internalLockLeaseTime = unit.toMillis(leaseTime);

    return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,
              "if (redis.call('exists', KEYS[1]) == 0) then " +
                  "redis.call('hset', KEYS[1], ARGV[2], 1); " +
                  "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                  "return nil; " +
              "end; " +
              "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
                  "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                  "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                  "return nil; " +
              "end; " +
              "return redis.call('pttl', KEYS[1]);",
                Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
}

참고

세마포어 vs 뮤텍스

https://afteracademy.com/blog/difference-between-mutex-and-semaphore-in-operating-system

optimistic lock vs pessimistic lock

https://enterprisecraftsmanship.com/posts/optimistic-locking-automatic-retry/

About Redis

https://redis.com/ebook/part-2-core-concepts/chapter-6-application-components-in-redis/

https://minholee93.tistory.com/entry/Redis-Transaction

https://jupiny.com/2020/03/28/redis-sorted-set/

https://hyperconnect.github.io/2019/11/15/redis-distributed-lock-1.html

@caffeine-library/readers-system-design-interview