동시성 문제 해결 방법으로 분산락을 선택하게 된 이유
우선 분산락을 처음 도입하게 된 과정은 커피챗 신청하기라는 기능에 대해서 동시에 여러명이 신청할 때 제한된 신청 인원수에 대한 동시성 처리를 하기 위해서였다. 하나의 서버만 존재할 때 Java의 synchonized라는 키워드를 통해 해결할 수 있지만, 해당 키워드만으로는 서버를 여러 대 사용하여 분산되어 있는 상황에서는 사용하기 어렵다는 이슈가 있다. 커피챗 신청 API는 API 서버에서 관리하는 기능으로 사용자가 많아지면 API 서버를 스케일 아웃하여 여러 서버에 대해서 운영될 수 있으므로 확장성 있는 방법을 선택하고자 분산락 처리 방법을 생각하게 되었다.
분산락은 여러 프로세스가 공유 데이터를 제어하기 위한 기술로 웹 애플리케이션 서버가 여러대이거나 스케일 아웃된 DB 환경에 대해서 동시성 문제를 해결하기 위한 방법이다. 우리가 고려한 상황은 이중에서도 웹 애플리케이션 서버가 여러 대일 때였는데 이런 상황에서 적용할 수 있는 분산락 방법은 낙관적 락, 비관적 락 혹은 ZooKeeper나 Redis 같은 다른 플랫폼을 사용해서 분산락을 구현하는 방법이 있었고, 각 방법의 장단점에 대한 비교와 더불어 이미 기술스택과 그 연관어 관리를 위해서 Redis를 사용하고 있었기 때문에 Redis를 이용한 분산락 구현 방식을 채택하게 되었다.
또 다른 분산락 처리 방식에 대해서 좀 더 설명해보자면.....
낙관적 락
다른 분산락 처리 방식에는 실제로 Lock을 사용하지 않고,버전을 통해 데이터의 정합성을 맞춘다.
- 낙관적 락은 에러가 발생하면 수동으로 롤백처리를 위한 로직을 구성해야 한다.
- 하지만 DB 단에서 별도의 Lock을 설정하지 않기 때문에 하나의 트랜잭션 작업이 길어질 때 다른 작업이 영향받지 않아서 성능이 좋을 수 있다.
비관적 락
트랜잭션이 시작될 때 DB에 Shared Lock 또는 Exclusive Lock을 걸고 시작하는 비관적 락,
- DB 단의 Lock을 통해서 동시성을 제어하기 때문에 확실하게 데이터 정합성이 보장된다.
- 비관적 락은 DB에 락을 걸기 때문에 한 트랜잭션 작업이 정상적으로 끝나지 않으면 다른 트랜잭션들이 대기해야하므로 성능이 떨어지게 되며 애플리케이션단에서 타임아웃을 설정하기 어렵다는 단점이 있다.
- 만일 Race Condition이 빈번하게 일어난다면 낙관적 락보다 성능이 좋다.
플랫폼을 이용한 분산락 구현
ZooKeeper나 Redis를 사용하는 방법.
다른 플랫폼을 이용한 분산락 처리의 가장 큰 단점은 플랫폼을 이용하기 위해서 별도의 인프라를 구축하고 관리해야한다는 점이다.
레디스로 분산락 처리하기
레디스로 분산락을 구현한 여러 라이브러리들이 있는데 대표적으로 lettuce와 Redisson이 있다.
lettuce는 Netty 기반의 Redis Client로 요청을 non-blocking으로 처리하는데 레디스에서 제공하는 원자적 연산인 setnx(SET if Not eXists) 를 활용하여 spin lock으로 구현하기 때문에 서버와 레디스가 감당해야하는 부하가 크다.
Reddison은 Pub/sub 방식을 이용한 분산락 처리로 스핀락보다 서버와 레디스에 부하가 적다.
pub/sub은 publisher는 발생한 이벤트에 대해서 채널에만 전파하고, 해당 채널을 구독하고 있는 subscriber들이 메세지를 읽어가는 방식을 말한다. 따라서 락을 가져갈 수 있다는 메세지를 채널에 전파하면 메세지를 받은 Subscriber 들이 락에 대한 점유를 시도하기 때문에 서버와 레디스에 부하를 덜 줄 수 있다. 또한 별도의 Lock Interface를 지원하기 때문에 락에 대한 타임아웃같은 설정을 편리하고 안전하게 사용할 수있다는 장점이 있다.
그래서 결과적으로 사용하기 편리하고 서버와 레디스에 부하를 덜 줄 수 있는 Redisson을 사용했다!
분산락을 위한 Lock Key의 기준
동시성 처리가 필요한 기능은 크롤러 수동 실행 API, 커피챗 신청 API가 있었다.
크롤러는 전체 서버 인스턴스 중에서 하나만 실행시키고자 key를 상수로 두었고,
커피챗은 커피챗마다 동시성 처리를 해야하므로 커피챗 id를 활용한 String을 키로 사용했다.
개선할 포인트는 언제나 있어요...ㅎ
우선 지금은 레디스를 한대만 쓰고 있기 때문에 레디스에 대한 SPOF가 존재한다. 레디스를 여러대 띄웠을 때 어떻게 동시성 처리를 할 것인지, 또 멀티 노드에서 동시성 처리에 성능 저하는 없는지 확인해봐야한다.........!!!!!!!
'Trouble Shooting' 카테고리의 다른 글
커버링 인덱스를 활용하여 페이지네이션 조회 속도 개선하기 (0) | 2024.07.17 |
---|---|
Layered Architecture 설계 (feat. SOLID) (0) | 2024.07.12 |
납득이 가는 멀티모듈 구조 도입기 (0) | 2024.07.06 |
브라우저에서 10개 이상의 동시 요청이 보내지지 않는다면 HTTP1.1을 사용하고 있지 않은지 확인해보세요 (^__ ^).. (0) | 2024.06.20 |
댓글