동시 접속자와 초당 요청량(TPS)
대규모 시스템을 설계할 때 얼마나 많은 사용자가 시스템을 사용할 것인지 파악하는 것이 중요하다.
가장 중요한 것은 동시 접속자의 요청 수이다.
TPS(Transactions Per Second)
초당 처리되는 트랜잭션의 수를 나타내는 지표이다. 시스템이 얼마나 많은 요청을 동시에 처리할 수 있는지를 나타내며, 시스템의 처리 능력을 가늠할 수 있게 해 준다.
시스템이 TPS을 견딜 수 있도록 설계해야 한다!
그리고 예상치 못한 이벤트로 인해 시스템이 TPS를 견디지 못해 중단되었을 경우의 상황도 대비해야 한다.
1. 애플리케이션의 수를 늘린다.
2. 오류 상황에서 사용자가 대기할 수 있도록 대기열을 설정한다.
3. 자동 스케일링을 통해 시스템의 자원을 동적으로 할당하여 부하를 분산시킨다.
요청 종류에 따른 개발
데이터 제공 및 저장에서 가장 많은 시간을 소모하는 부분은 대부분 DB에서 데이터를 조회하거나 쓰는 것이다. (Input, Output)
읽기 요청 최적화
- 캐시 사용
캐시를 사용하면 데이터를 빠르게 제공할 수 있으며, DB의 부하를 줄일 수 있다.
게이트웨이에서 캐싱을 처리하면 시스템의 요청 처리량이 줄어들어 적은 수의 애플리케이션으로도 요청을 처리할 수 있다.
캐시를 사용할 땐 캐시 갱신 정책이 중요하다. 예를 들면 변경이 잦은 경우 짧은 유효 기간을 설정하거나, 변경 시점에 캐시를 무효화하여 최신 데이터를 제공할 수 있도록 해야 한다.
레디스 캐시를 사용할 때 조심해야 할 것!
=> 메모리가 초과되면 캐시가 작동을 멈추거나 데이터가 손실될 수 있다. Redis가 사용할 수 있는 메모리의 상한선을 정하거나, 유효 기간 TTL(Time to Live)을 설정하여 오래된 데이터가 자동으로 제거되도록 해야 한다. 또한, 지속적인 모니터링 도구를 통해 일정량의 메모리를 넘었을 경우 알림을 보내서 개발자가 캐시 에러 발생 전에 처리할 수 있도록 해야 한다.
- 데이터 베이스 사용 최적화
1. 데이터 베이스 인덱싱을 통해 조회 성능을 향상할 수 있다.
하지만 인덱스가 너무 많으면 쓰기 성능이 저하될 수 있다는 문제가 있다.
2. 데이터 베이스 샤딩을 통해 DB를 여러 샤드로 분할하여 각각의 샤드가 독립적으로 쿼리를 처리하도록 하는 방법이 있다. 이를 통해 읽기 요청에 대한 응답 속도를 향상할 수 있다. ex) 테이블 파티셔닝
3. 읽기 전용 데이터베이스를 사용하면 주 DB에 대한 읽기 부하를 줄일 수 있다.
하지만 이때 쓰기 DB와 읽기 DB에 대한 정보 차이가 있을 수 있다. 이 문제를 해결하기 위해 쓰기 DB를 수정할 때 읽기 DB의 캐시를 동시에 업데이트해주면 된다.
4. 쿼리 최적화
쓰기 요청 최적화
- 비동기 처리
쓰기 요청을 비동기 방식으로 처리하면 DB에 직접 접근하지 않고도 빠르게 응답을 반환할 수 있다.
예를 들어 메시지를 바로 DB에 쓰지 않고, 큐에 넣어 나중에 처리하는 방법이 있다.
- 배치 처리
실시간으로 처리할 필요가 없는 쓰기 요청은 배치 처리를 통해 한꺼번에 처리할 수 있다.
예를 들어 일정 시간마다 큐에 쌓인 메시지를 DB에 쓰는 방법이 있다. 매일 자정에 하루 동안 수집된 로그 데이터를 한 번에 DB에 저장하는 방식으로 사용할 수 있는 것이다.
- 분산 DB
단일 DB로 모든 쓰기 요청을 처리하기 어렵다면, 분산 DB를 사용하여 부하를 분산시킬 수 있다.
예를 들어 여러 개의 DB 인스턴스를 사용하여 각 인스턴스가 특정 사용자 그룹의 데이터를 처리하도록 할 수 있다.
하지만 분산 DB 사용 시 데이터 일관성을 유지하기 위해 트랜잭션 관리와 데이터 동기화에 신경 써야 한다.
데이터 일관성 유지
분산 트랜잭션
분산 트랜잭션이란 여러 개의 독립된 시스템이나 DB에서 일어나는 트랜잭션을 일관되게 관리하는 방법이다.
ex) MSA나 DB에서 데이터를 동시에 업데이트하는 경우가 분산 트랜잭션 작업에 해당된다.
- 2PC (Two-Phase Commit)
분산 트랜잭션을 관리하는 프로토콜이다. 2가지 단계로 나누어 트랜잭션을 처리한다.
1) 준비 단계 : 각 참여 노드는 트랜잭션 준비 상태를 확인하고, 준비 완료를 마스터 노드에 알린다.
2) 커밋 단계 : 마스터 노드는 모든 참여 노드가 준비되었음을 확인하고, 트랜잭션을 커밋하도록 지시한다. 준비되지 않은 참여 노드가 있다면 트랜잭션을 롤백한다.
- 사가 패턴 (Saga Pattern)
트랜잭션을 여러 단계로 나누어 처리하고, 각 단계가 독립적으로 커밋된다.
- 이벤트 소싱 (Event Sourcing)
상태 변화를 이벤트로 기록하고, 해당 이벤트를 재생하여 분산 트랜잭션의 일관성을 유지한다.
분산 트랜잭션의 예시
1) 주문 생성과 결제 처리 예시
한 사용자가 온라인 쇼핑몰에서 주문을 생성하고, 결제를 진행한다. 이 과정에서 주문 서비스와 결제 서비스는 각각 독립된 데이터베이스를 사용한다.
=> 분산 트랜잭션을 통해 두 서비스가 일관되게 주문과 결제를 처리해야 한다. 두 작업이 정상 처리되면 트랜잭션이 커밋된다. 한 작업에서 에러가 일어나면 전체 트랜잭션이 롤백되어 주문과 결제 정보가 모두 저장되지 않도록 한다.
이벤트 소싱
이벤트 소싱은 데이터 상태 변화를 이벤트로 기록하고, 해당 이벤트를 순차적으로 재생하여 현재 상태를 파악하는 방법이다.
즉, 결과에 대한 모든 과정을 이벤트로 저장하는 것이다.
이벤트 소싱의 장점은 모든 상태 변화를 이벤트로 기록하므로, 데이터 변경 이력을 완벽하게 추적할 수 있다는 것이다.
단점은 그만큼 시스템 설계와 구현의 복잡성이 증가할 수 있다는 것이다.
CQRS
CQRS는 명령과 조회의 책임을 분리하는 소프트웨어 디자인 패턴이다.
- 명령 : 데이터를 변경하는 작업. 상태를 변경하는 비즈니스 로직을 처리함.
- 조회 : 데이터를 읽는 작업. 상태를 변경하지 않고, 필요한 데이터를 반환함.
이 패턴은 읽기 작업과 쓰기 직접을 서로 다른 모델로 분리하여, 각 작업에 최적화된 구조를 사용할 수 있도록 한다.
CQRS의 장점은 읽기와 쓰기를 독립적으로 확장할 수 있다.
단점은 그만큼 복잡성이 증가하고, 데이터 동기화를 위한 추가적인 구현이 필요하다.
테스트와 배포
테스트
- 단위 테스트 (Unit Test)
시스템의 개별 구성 요소를 테스트하여 각 부분의 동작을 테스트한다.
ex) Junit
- 통합 테스트 (Integration Test)
여러 구성 요소가 함께 동작하는지를 테스트한다.
ex) @SpringBootTest
- 부하 테스트 (Load Test)
시스템이 높은 트래픽 상황에서도 안정적으로 동작하는지를 테스트 한다. (TPS 체크)
ex) JMeter와 같은 도구를 사용해서 부하 시나리오를 설정하고 테스트를 해야 한다.
- 회귀 테스트 (Regression Test)
새로운 코드 변경이 기존 기능에 영향을 미치지 않는지 확인한다.
기존 테스트 케이스를 자동화하여 주기적으로 실행함으로써, 코드 변경으로 인한 결함을 방지할 수 있다.
- 사용자 수용 테스트 (UAT, User Acceptance Test)
실제 사용자 환경에서 시스템을 테스트하여, 사용자 요구 기능이 모두 제대로 동작하는지 확인한다.
사용자 수용 테스트는 시스템이 실제 운영 환경에서 기대대로 동작하는지 확인하는 중요한 단계이다.
배포
- 지속적인 통합 (CI)
개발자가 변경한 코드를 자동으로 빌드하고 테스트하여, 코드 변경 시점에서 발생할 수 있는 문제를 조기에 발견하고 해겷나다.
코드 통합을 빠르고 효율적으로 수행할 수 있다.
- 지속적인 배포 (CD)
CI 파이프라인을 통해 검증된 코드를 자동으로 배포한다.
CD는 코드 변경 사항을 빠르고 안전하게 배포 환경에 적용할 수 있다.
- Canary 배포
새로운 버전을 전체 시스템에 배포하기 전에, 일부 사용자에게만 배포하여 문제가 없는지 확인한다.
문문제가 발생할 경우 빠르게 이전 버전으로 롤백할 수 있다.
- 블루-그린 배포
두 개의 환경 (블루와 그린)을 사용하여 하나는 현재 운영 중인 환경이고, 다른 하나는 새로운 버전을 배포하는 환경으로 사용한다.
새로운 버전을 그린 환경에 배포한 후 , 문제가 발생하면 블루 환경으로 빠르게 롤백할 수 있다.
- 롤링 배포
새로운 버전을 점진저긍로 배포하여, 각 서버를 순차적으로 업데이트한다.
롤링 배포는 대규모 시스템에서 무중단 배포를 구현하는 데 유용하다.
'Backend > MSA' 카테고리의 다른 글
Kafka TIL (0) | 2025.01.01 |
---|---|
RabbitMQ TIL (0) | 2025.01.01 |
241220 시큐어 코딩 개념과 실습 TIL (1) | 2024.12.20 |
241219 모니터링 시스템 개념과 실습 TIL (0) | 2024.12.19 |
241126 이벤트 드리븐 아키텍처와 스트림 처리 (Spring Cloud Stream) TIL (0) | 2024.11.26 |