05-02 동시성 제어
Q. 동시에 여러 사용자가 같은 ID로 회원가입을 시도할 때 발생할 수 있는 문제점은 무엇이며, 어떻게 해결 할 수 있을까요?
동시에 여러 사용자가 같은 ID로 회원가입을 시도하는 상황에서는 Race Condition
이 발생할 수 있습니다.
예를 들어, A와 B 사용자가 동시에 'user123'이라는 ID로 가입을 시도한다고 가정했을때, 두 사용자가 거의 동시에 ID 중복 체크를 하면, 둘 다 해당 ID가 존재하지 않는다고 판단하고 회원가입을 진행할 수 있습니다. 이런 경우 데이터 정합성이 깨질 수 있습니다. 이를 해결하기 위한 방법으로는 크게 세 가지를 고려할 수 있습니다.
첫째, 가장 쉬운 방법으로 데이터베이스 컬럼에 유니크 제약조건을 설정하는 방법입니다. ID 컬럼에 unique constraint
를 걸어두면, 중복 데이터 삽입 시도 시 에러가 발생하여 하나의 회원만 가입되도록 보장할 수 있습니다.
두번째, 애플리케이션 레벨에서 ConcurrentHashMap
을 사용하여 atomic
한 처리를 할 수 있습니다. putIfAbsent()
메소드를 활용하면 체크와 등록을 원자적으로 수행할 수 있어서 안전합니다.
세번째, MSA같은 분산 환경에서는 Redis
를 활용한 분산 락을 고려할 수 있습니다.
실제 프로젝트에서는 이러한 방법들을 상황에 맞게 조합하여 사용하는 것이 좋습니다. 저는 주로 데이터베이스의 유니크 제약조건을 기본으로 하고, 추가적인 안정장치로 애플리케이션 레벨에 ConcurrnetHashMap
을 구현하는 방식을 선호합니다.
Q. Redis 분산락에 대해 조금 더 자세히 설명해주세요.
Redis 분산락
은 여러 애플리케이션 인스턴스들이 공유 자원에 동시에 접근하는 것을 제어하기 위해 Redis를 활용하는 동시성 제어 메커니즘입니다.
락을 획득하려는 클라이언트는 Redis의 SET
명령어를 사용하는데, 이때 NX(Not eXists)
옵션을 통해 키가 존재하지 않을 때만 값을 설정하도록 합니다. 이 연산은 원자적으로 수행되며, 락의 값으로 클라이언트 고유 식별자(예: UUID)를 설정하여 소유자를 식별합니다. 동시에 PX(밀리초)
또는 EX(초)
옵션으로 락의 만료 시간(TTL)
을 설정하여, 클라이언트가 비정상 종료되더라도 락이 영원히 유지되는 데드락 상황을 방지합니다.
락을 획득한 클라이언트는 공유 자원 작업을 수행한 후, Redis에서 해당 락 키를 삭제(DEL)
하여 락을 해제합니다. 이때 중요한 점은, 자신이 설정한 락인지 확인하기 위해 락 값에 고유한 ID를 포함시키고, Lua 스크립트를 사용하여 "키의 값이 내가 설정한 값과 일치하면 키를 삭제하라" 는 원자적인 연산을 수행하는 것입니다. 이는 다른 클라이언트가 획득한 락을 실수로 해제하는 문제를 방지합니다.
Redis는 인메모리 데이터 스토어로, 락 작업이 빠르고 구현이 간단하다는 장점이 있습니다. 그러나 단일 인스턴스에서는 안정적이지만, 클러스터 환경에서는 네트워크 분할로 인한 스플릿 브레인
문제가 발생할 수 있습니다.
Q. 만약 Lua 스크립트를 사용하지 않고, 애플리케이션에서 GET
명령으로 락의 소유자를 확인한 뒤 DEL
명령으로 락을 삭제하는 방식을 사용한다면 구체적으로 어떤 위험이 발생할 수 있을까요?
GET
명령으로 락의 소유자를 확인한 뒤 DEL
명령으로 락을 삭제하는 방식을 사용한다면 구체적으로 어떤 위험이 발생할 수 있을까요?Lua 스크립트
없이 GET
과 DEL
을 순차적으로 실행하면, 두 명령어 사이의 짧은 시간 틈 때문에 동시성 문제가 발생할 수 있습니다.
예를들어, 클라이언트 A가 락 해제를 위해 먼저 GET
명령으로 자신이 락의 소유자임을 확인했다고 가정해보겠습니다. 하지만 바로 그 직후, A가 DEL
명령을 보내기 전에 A가 가진 락의 TTL
이 만료될 수 있습니다. 그리고 그 찰나의 순간에 클라이언트 B가 동일한 락을 새롭게 획득할 수 있죠.
결과적으로 A는 이 사실을 모른채 DEL
명령을 실행하게 되고, 자신이 소유하지 않은, 즉 B가 막 획득한 락을 삭제해버리는 상황이 발생할 수있습니다. 이렇게 되면 B는 락의 보호를 받지 못한 채 작업을 수행하게 되어 데이터가 오염될 수 있습니다.
Lua 스크립트
는 이러한 '확인 후 삭제' 과정을 하나의 원자적 연산으로 묶어주기 때문에 이런 위험을 원천적으로 차단할 수 있습니다.
Last updated