최근, DB관련 공부를 진행하며 이전부터 동시성을 제어할 수 있는 방법으로 LOCK을 사용할 수 있음을 알고 있었는데,
위 내용을 현재 제가 사용하고 있는 Spring Boot에 접목시켜 봄으로써 이에 대한 리마인드와 저의 생각들을 정리해 보고자 글을 작성하게 되었다.
동시성을 그렇다면 왜 제어해야 할까요?
동시성을 제어하지 않는다면 발생되는 이유는 다음과 같습니다.
위와 같은 문제를 방지하기 위해 동시성제어를 통해 보다 안정적인 서비스를 제공할 수 있습니다.
네임드 락의 경우 MySQL에서 사용되는 락 방법 중 하나로서, 이름을 가진 메타 데이터 락이다.
이름을 가진 락을 획득 후, 해제될 때까지 다른 세션은 해당 락을 획득하지 못한다.
여기서 주의해야 할 점은, 트렌잭션이 만료되면 락이 자동으로 해제되지 않기에 락을 해제시켜 주거나 선점시간이 종료되어야 해제된다는 특징을 가지고 있다.
MySQL에서는 GET_LOCK()을 통해 락을 획득할 수 있고, RELEASE_LOCK()을 통해 해제될 수 있다.
네임드락(GET_LOCK)의 사용 용도는 주로 분산 락에서 사용된다.
다음 사진을 보며 설명을 이어가 보고자 한다.
해당 그림을 보면 Session-1 , Session-2가 Stock에 동시적으로 요청을 보낼 시, 이때 락을 Stock에 거는 것이 아닌, 별도의 공간에 Lock을 걸게 된다.
이렇게 Lock을 걸게 되면 Session-2는 Session-1이 ‘1’이라는 이름으로 LOCK을 걸게 된다면 Session-2는 Session-1이 해제된 이후에 LOCK을 획득할 수 있게 된다.
자바 스프링 기반의 웹 애플리케이션은 기본적으로 멀티 스레드 환경에서 구동이 된다.
따라서, 여러 스레드가 함께 접근할 수 있는 공유 자원에 대해 Race condition이 발생하지 않도록 별도의 처리가 필요하다.
자바는 synchronized라는 키워드를 언어차원에서 제공하여, 모니터 기반의 상호배제 기능을 제공하게 된다.
하지만, 이런 메커니즘은 같은 프로세스에서만 상호 배제를 보장한다.
웹 애플리케이션 프로세스를 단 하나만 사용하는 서비스라면 상관없지만, 대부분 일반적으로 서버를 다중화하여 부하 분산 처리를 한다.
이러한 분산 환경 속에서 상호 배제를 구현하여 동시성 문제를 다루기 위해 등장한 방법이 바로 분산락이다.
여기서 알아야 할 사항은 분산락은 DB에서 제공하는 락의 종류가 아니다.
일반적으로 웹 애플리케이션에서 공유 자원으로 데이터베이스를 가장 많이 사용하기에, 데이터베이스에 대한 동시성 문제를 분산락으로 풀어내는 사례가 많을 뿐이다.
물론, DB의 락 기능을 활용하여 분산 락을 구현할 수 있지만, 직접적으로 연관 있다고 보기는 어렵다.
분산 락을 구현하기 위해 락에 대한 정보를 ‘어딘가’에 공통적으로 보관하고 있어야 한다. 그리고 분산 환경에서 여러 대의 서버들은 공통된 ‘어딘가’를 바라보며, 자신이 임계 영역(critical section)에 접근할 수 있는지 확인한다.
이렇게 분산 환경에서 원자성(atomic)을 보장할 수 있게 된다. 그리고 그 어딘가로 활용되는 기술은 MySQL의 네임드 락, Redis, Zookeeper 등이 있다.
대체적으로 분산락을 Redis를 활용하여 많이 제어하고 있는데, 왜 그럴까?
Spring Boot에서는 Lettuce, Redisson을 활용하여 Redis 내 분산 락을 제어할 수 있다.
하지만, 대개로 Redisson을 많이 활용하는데 이유가 무엇이었을까?
Redisson 내부에서 락을 잡기 위해 사용되는 tryLock() 메서드 내부를 살펴보자
해당 메서드의 구현체를 살펴보겠다.
tryLock() 내부를 살펴보면 다음과 같이 subscribe 메서드를 찾을 수 있는데 이 메서드는 위 사진처럼 pub/sub 기능을 활용하고 있음을 확인할 수 있었다.
해당 메서드의 구독이 성공하게 될 경우 Redis 서버는 지정한 채널에 대한 메시지가 발행되는 경우 클라이언트에게 메시지를 전송하게 됩니다.
그렇다면 이 내부는 어떻게 구성이 되어있을까요?
다음 사진은 위 pubsub 내, subscribe 메서드 내부입니다.
해당 부분까지 확인하였을 때, tryLock()을 활용할 경우 위 내용처럼 Semaphore를 활용하고 있음을 확인하고 있었습니다.
그렇구나! 여기까지 살펴보았을 때 다음과 같은 플로우로 진행됨을 확인할 수 있었습니다.
이제 해당 개념을 살펴보았으니, 예제를 통해 실습을 진행해 보았다.
동시성이 발생하는 시나리오를 다음과 같이 구상해 보았다.
한번 위 상황을 고려하여 구현을 하였다. 내용이 너무 길어질 것 같아, 구현내용은 생략하고 테스트 코드를 통해 살펴보겠다.
해당 구현 내용은 깃허브 링크를 첨부합니다 :)
https://github.com/JoeCP17/spring-study/tree/master/lock
DB에 대한 공부를 계속 진행해 보며 이전부터 건수님이 작성해 주셨던 분산락을 재미있게 읽고 이를 자세하게 공부해 보는 시간을 가져보았습니다. 🙂
해당 개념을 다시 한번 공부해보며 이전 RealMySQL에서 읽었던 각 Lock의 대한 개념들과 Redisson의 실행 구조와 왜 사용하는지에 대해 다시한번 공부를 할 수 있었던 좋은 시간이었습니다.
꼭 분산락이 아니더라도 Lock의 경우 정말 사용해야 할 경우가 아닌 경우에 사용하게 될 시, 성능저하의 이슈가 있을 수 있기 때문에 조금 더 심도 있게 고민해야 할 수 있음을 공부하며 다시 한번 느낄 수 있었습니다.
아직까지는 실무에 적용을 시켜볼 일이 없지만 앞으로의 일정들과 기획에 따라 사용해야 할 경우 접목시킬 수 있도록 더 갈고닦아야겠다고 생각했습니다.
Redis로 분산 락을 구현해 동시성 이슈를 해결해 보자!
재고시스템으로 알아보는 동시성이슈 해결방법 - 인프런 | 강의
빗썸 API를 활용한 매수 / 매도 데이터 적재 (1) | 2023.06.11 |
---|---|
[Spring Boot + Chat GPT] Open AI API 적용기 (1) | 2023.04.16 |
Throttling과 debounce에 대해 알아보자 (0) | 2022.12.11 |
[Design Pattern] Strategy Pattern 전략패턴에 대해 알아보자! (0) | 2022.10.30 |
[DI,DIP] 의존성 주입에 대하여 (0) | 2022.10.12 |