Releases: boostcampwm-2024/refactor-web36-QLAB
Releases v1.0.0
서비스 아키텍처
목표 아키텍처
쿠버네티스 아키텍처
사용자 쿼리 실행 성능 개선(DB 부하 분산)
문제 : DB 부하
- 사용자에게 DB 를 할당하고 사용하게 하는 서비스인 만큼, DB 성능이 중요합니다.
- 테스트 해 본 결과에서도 DB 의 부하가 상당히 크다는 것을 확인할 수 있었습니다.
학습 : DB 수평확장
DB의 성능을 늘리기 위해 확장할 수 있는 방법이 무엇이 있나 고민하였고, 이에 대해 정리하였습니다.
DB 수평확장과 쿠버네티스를 활용하여 부하를 분산할 아키텍처를 고민하였고, 목표 아키텍처를 세웠습니다.
해결방안
샤딩을 통한 부하분산에 아이디어를 얻어 MySQL 서버를 4개 올리고, 각 사용자의 DB 를 분산하는 방법을 택하였습니다. 사용자의 데이터베이스를 각 샤드에 분산하여 저장합니다.
로드밸런싱 구현
서버가 많아짐에 따라 새로운 사용자에 적절한 DB 서버를 할당해야 했습니다.
활성사용자 수를 기준으로 가장 적은 DB 서버 에 할당되도록 구현하였습니다.
- 활성사용자 수는 DB 서버 별로 최근 5분 안에 요청을 보낸 사용자 수 입니다.
기존 사용자가 접속할 때는 할당된 DB에 커넥션을 맺어주어야 합니다.
이를 위해서 Sticky Session을 활용하였습니다.
활성 사용자 관리
문제 : 레디스 풀 스캔
위의 방식으로 로드밸런싱을 구현하면, 새로운 사용자가 접속하였을 때 모든 사용자를 한번 읽어서 각 DB 서버 별 활성 사용자를 계산해야 합니다. 새로운 사용자가 접속할 때마다 레디스를 풀스캔하게 되는 문제가 있습니다.
해결 방안 : 활성 사용자 수 값을 관리
이를 해결하기 위해 레디스에서 각 DB 서버 별 활성 사용자 수를 별도로 관리할 데이터베이스를 만들고, 어플리케이션 서버에서 활성 사용자 수를 관리하고자 하였습니다.
활성 사용자 수 값에 대한 무결성 문제
문제 : 동시성 문제
활성 사용자를 관리하기 위해 사용자 요청이 있을 때 레디스의 값을 업데이트하게 됩니다. 이 경우, 사용자 요청이 빈번할 때 활성 사용자 값이 오버라이딩 되는 문제가 우려되었습니다.
해결 방안 : write-back
이는 사용자가 많을때 매우 자주 일어날 수 있는 이벤트이므로 redis에 값을 자주 쓰기 작업을 수행하게 됩니다. 그러나 싱글스레드 기반인 레디스 특성 상 바람직 하지 않고 실시간으로 업데이트 해서 동기화 해주어야 할 만큼 중요한 데이터는 아니라고 판단하였습니다.
그래서 인메모리 변수에서 증감값을 가지고 있고 주기적으로(1분마다) Cron 작업을 통해 값을 레디스에 반영한다.
이벤트 처리 로직의 중복
문제 : 구독으로 인한 로직 중복 처리
Redis pub/sub은 발행을 할 시 구독한 서버에게 모두 이벤트를 전송하는 방식으로 동작합니다.
따라서 pm2를 사용함에 따라 생성된 모든 서버가 동일한 이벤트에 대한 로직을 중복하여 실행하는 문제가 발생하였습니다.
해결 방안 : 서버분리
API-Server와 DB-Manager-Server를 분리하여 세션 생성/만료에 대한 이벤트를 DB Manager만 구독하여 처리하도록 하였습니다. 새로운 사용자에 대해 DB를 생성하고, 삭제하며, DB 서버를 로드밸런싱하여 할당하는 로직을 DB 매니저 서버에서 수행하도록 하였습니다.
API-Server
- 사용자 API 요청에 대한 처리
DB-Manager-Server
- 세션 생성시 활성 사용자가 가장 적은 서버에 DB 생성
- DB 삭제
- 활성 사용자 관리