삽질블로그

치킨 디도스 프로젝트(3) 본문

개발

치킨 디도스 프로젝트(3)

삽질블로그 2024. 9. 4. 18:07

목차

1. 프로젝트 개요

  • 1-1. 프로젝트 소개
  • 1-2. 문제 해결 전략
  • 1-3. 기술 스택
  • 1-4. 데이터베이스 ERD

2. 성능 개선

  • 2-1. 비관적 락 vs 낙관적 락
  • 2-2. Redis를 이용한 분산락
  • 2-3. Redis 성능 향상
  • 2-4. 그래프

3. 개선 사항

 

 

1. 프로젝트 개요

1-1. 프로젝트 소개

 

선착순으로 치킨 쿠폰을 받을 수 있는 치킨 디도스 프로젝트입니다. 첫 번째 분산 시스템 프로젝트입니다.

 

  • 프로젝트 요구사항
쿠폰 발급 이벤트를 진행합니다.
유저는 다음과 같은 종류의 쿠폰 중 하나를 선택하여 발급 받을 수 있습니다.

  - 쿠폰 A: 치킨 무료쿠폰, 선착순 300장, 쿠폰코드: A001, 할인율 100%
  - 쿠폰 B: 5천원 할인, 선착순 500장, 쿠폰코드: B001, 할인율 50%
  - 쿠폰 C: 1천원 할인, 천착순 1000장, 쿠폰코드: C001, 할인율 10%

유저는 선착순으로 쿠폰을 발급받을 수 있습니다. 중복 발급은 불가합니다.

 

  • 이 프로젝트의 핵심 아이디어만 구현하기 위해 다음과 같은 가정을 합니다.
1. MSA 아키텍처라고 가정합니다. 사용자 인증/인가 등의 추가 기능은 포함되지 않습니다.
2. 쿠폰의 정보는 미리 데이터베이스에 들어가 있다고 가정합니다.

 

 

1-2. 문제해결전략

 

치킨 이벤트의 특성을 파악해야 합니다.

  1. 특정 시간부터 짧은 시간에 막대한 트래픽이 서버에 가해짐
  2. 짧은 시간에 발생하는 쿠폰 발급은 중복 발급, 제한 수량 등 정확한 데이터가 요구됨

이런 특성을 고려하여, 분산 시스템을 기반으로 한 백엔드 시스템을 구성하였습니다.

  1. 하나의 쿠폰 발급을 위해 수 많은 유저가 동시에 접근하는 이런 서비스에서는 동시성을 제어하는게 필수적입니다. 따라서 이런 문제를 해결하기 위해 초기에는 외부 자원을 사용하지 않고 데이터베이스의 자체 락을 사용하는 방식을 고려했습니다.
  2. 하지만 여기서 성능을 더 개선시키기 위해 인메모리 방식인 Redis를 사용하여 분산락을 사용하는 방식으로 수정하였습니다. 

 

1-3. 기술 스택

  • Language: Typescript
  • Framework: Nest.js
  • Database: Mysql
  • Cache: Redis, Redlock
  • Infra: Docker
  • Test: Jest, K6
  • Monitoring: Prometheus, Grafana

 

1-4. 데이터베이스 ERD

ERD

 

2. 성능 개선

10000명의 가상 유저가 60초 동안 300개의 쿠폰을 발급받기 위해 트래픽을 부하할 때를 기준으로 진행했습니다.

 

2-1. 비관적 락(사용) VS 낙관적 락

 

이번 프로젝트를 진행하면서 동시성 제어를 해결하기 위해 데이터베이스 자체에서 락을 거는 방식을 고려하였습니다.

동시성을 제어하기 위해서 비관적 락과 낙관적 락 중에서 뭘 사용해야 할까 고려해봤을 때

동시에 수 많은 트래픽이 몰리기 때문에 충돌이 불가피하다고 생각하여 비관적 락을 사용하기로 결정했습니다.

비관적 락

TPS : 139000 / 60 = 2316

MTT : 3.32s

 

데이터베이스 자체에서 비관적 락을 사용하여 동시성 제어를 했을 때 다음과 같이 발급된 쿠폰 수량이 300개가 정상적으로 발급된 것으로 보아 동시성 제어가 성공적으로 진행된 것을 확인할 수 있습니다.

하지만 각 요청이 완료되는 시간인 MTT가 3.32초로 아쉬운 성능임을 확인할 수 있습니다. 

따라서 TPS와 MTT를 개선하기 위해 이보다 더 빠른 캐시를 이용하여 분산락을 통해 성능을 개선하게 되었습니다.

 

 

2-2. Redis를 이용한 분산락

 

분산 락

TPS : 115000 / 60 = 1916

MTT: 2.26s

 

Redis를 이용한 분산락을 사용하였을 때 결과를 모니터링한 이미지입니다.

마찬가지로 300개의 쿠폰이 정상적으로 발급된 것으로 보아 동시성 제어가 성공적인 것을 확인할 수 있고

비관적 락에 비해 MTT가 개선된 것을 확인할 수 있지만 TPS가 낮아지는 것을 확인할 수 있습니다.

 

저는 Redis의 Redlock을 사용했는데 이는 여러 Redis 객체를 이용하게 됩니다. 

TPS가 낮아지는 이유가 Redis lock 객체를 초기화할 때 해둔 초기 설정을 할 때

락을 획득하기까지의 과정에 불필요한 리소스 낭비가 있다고 판단하여

초기 설정을 조절하여 네트워크 오버헤드를 줄이고 다음과 같은 개선을 하였습니다.

 

 

2-3. Redis 성능 향상

분산락 개선 후

TPS : 198000 / 60 = 3300

MTT : 964.34ms

 

Redis lock 객체를 초기화할 때 락을 획득하기 위한 재시도 횟수를 줄이고, 재시도  사이에 대기 시간과 재시도 시 더해지는 랜덤 시간을 조절하였습니다. 결과적으로 TPS가 3300, MTT가 1초 미만으로 성능이 많이 좋아진 것을 확인할 수 있습니다.

 

 

2-4. 그래프

성능 개선

 

3. 개선 사항

 

3-1. k8s를 통한 로드밸런싱

 

현재 이 서버의 자원을 통해 어떻게 성능을 더 향상시킬 수 있을까 생각한 결과 k8s를 사용하여 서버의 pod를 늘려 로드밸런싱을 활용하면 성능을 향상시킬 수 있을까? 하는 생각에 진행해봤지만 MTT는 향상되는 반면 TPS가 낮아지는 것을 확인하였습니다.

추후에 이를 모니터링하여 어떤 부분에서 문제가 발생하는지 확인하고 개선하는 것을 목표로 하고있습니다.

 

3-2. Message Queue 활용

 

메시지 큐를 활용한 대기열 처리를 목표로 하고 있습니다.

 

3-3. CPU 사용률 감소

 

현재 CPU 사용률이 극적으로 증가하는 것을 확인하였는데, 이를 모니터링하여 개선하는 것을 목표로 하고 있습니다. 

 

3-4. Redis Cache 사용

 

Redis를 이용하여 분산락을 사용하고 있지만 캐시를 사용하고 있지 않아서 DB에 부하가 예상되는 상황입니다. 

이를 Cache를 통해 데이터에 접근할 수 있도록 바꿔 성능을 개선하는 것을 목표로 하고 있습니다.

'개발' 카테고리의 다른 글

치킨 디도스 프로젝트(2)  (2) 2024.09.03
치킨 디도스 프로젝트(1)  (0) 2024.08.02
NestJS 예외 핸들링  (1) 2024.07.22
개인 프로젝트 후기  (3) 2024.07.19
Docker와 Jenkins를 이용해서 CI/CD를 구축해보자  (1) 2024.04.16
Comments