대규모 시스템
1. 동시 접속자와 초당 요청량(TPS)
(1) TPS란?
- 초당 처리되는 트랜잭션의 수를 나타내는 지표입니다. 이는 시스템의 성능을 평가하는 중요한 지표 중 하나로, 특히 대규모 시스템에서 중요한 역할을 합니다.
(2) 시스템이 초당 요청량(TPS, Transactions Per Second)을 견딜 수 있어야 합니다.
- 일간 접속량이 아닌, 특정 시간대에 초당 접속자 요청량이 가장 많은 시간을 파악해야 합니다. 이 정보는 시스템의 용량 계획을 세우는 데 매우 중요
- 예를 들어 기존 시스템이 초당 200건의 요청을 처리했다고 할때, 이를 기반으로 시스템의 용량을 결정해야하며 우리는 200건을 기준으로 1.5배인 300건을 처리할 수 있도록 시스템을 설계하는 것이 바람직합니다.
(3) 예상치 못한 이벤트로 인해 설계 예상 이상의 요청이 몰린다면 시스템이 중단될 수 있습니다.
- 이를 해결하기 위해 애플리케이션의 수를 늘리는 방법이 있습니다.
- 오류 상황에서 사용자가 대기할 수 있도록 대기열을 설정하는 방법도 있습니다.
- 자동 스케일링을 통해 시스템의 자원을 동적으로 할당하여 부하를 분산시키는 것도 중요
2. 요청 종류에 따른 개발
- 시스템이 읽기 전용인지, 쓰기 및 업데이트를 위한 것인지도 중요합니다. 이를 파악하고 처리 속도를 빠르게 하여 응답한다면 보다 많은 사용자를 수용할 수 있습니다.
(1) 읽기 요청 최적화
1️⃣ 캐시 사용
- 데이터에서 필터된 데이터를 가져오는것보다 레디스에서 필터된 데이터를 가져오거나 애플리케이션 로직에서 필터링을 수행하는것이 더 큰 성능상의 이점을 가질 것
- 캐싱 전략을 설계할 때는 캐시 갱신 정책도 중요합니다. 예를 들어, 공지사항이 자주 변경되지 않는 경우에는 캐시의 유효 기간을 길게 설정할 수 있습니다. 반면, 변경이 잦은 경우에는 짧은 유효 기간을 설정하거나, 변경 시점에 캐시를 무효화하여 최신 데이터를 제공할 수 있도록 해야 합니다. 이러한 캐싱 전략을 통해 시스템의 효율성을 극대화할 수 있습니다.
- 데이터 소실의 위험을 줄이기 위해 데이터의 유효성을 지속적으로 검증하고, 데이터가 손실되지 않았는지 확인해야 합니다. 또한, 캐시 계층에서 데이터 소실 시 재요청을 통해 데이터베이스에서 데이터를 다시 가져올 수 있도록 해야 합니다.
2️⃣ 데이터 베이스 사용 최적화
- 데이터베이스 인덱싱 : 데이터베이스 인덱싱은 조회 성능을 크게 향상시킬 수 있는 방법입니다. 인덱스를 사용하면 데이터베이스는 데이터를 빠르게 검색할 수 있습니다.
- 데이터베이스 샤딩 : 데이터베이스를 여러 샤드로 분할하여 각각의 샤드가 독립적으로 쿼리를 처리하도록 하는 방법
- 읽기 전용 데이터베이스 : 데이터를 주기적으로 동기화하여 최신 상태를 유지하면서 읽기 요청을 처리합니다. 이 방법은 주 데이터베이스에 대한 읽기 부하를 줄이고 성능을 최적화
- 쿼리 최적화 : SQL 쿼리를 효율적으로 작성하여 데이터베이스의 읽기 성능을 향상시키는 방법입니다. 예를 들어, 불필요한 조인을 줄이고, 필요한 컬럼만 선택하며, 적절한 조건을 사용하는 것이 중요
(2) 쓰기 요청 최적화
- 쓰기에서 가장 많은 시간을 소요하는 부분은 DB에 데이터를 생성하는 부분입니다. 이를 해결하기 위한 다양한 방법이 있습니다.
1️⃣ 비동기 처리
- 쓰기 요청을 비동기 방식으로 처리하면 DB에 직접 접근하지 않고도 빠르게 응답을 반환할 수 있습니다
- 예를 들어 메시지를 바로 DB에 쓰지 않고, 큐에 넣어 나중에 처리하는 방법이 있습니다
- 비동기 처리 시 데이터 소실이나 오류를 방지하기 위해 큐에 데이터를 넣을 때 적절한 검증을 수행하고, 큐에 쌓인 데이터를 지속적으로 모니터링하여 실패한 요청을 재시도할 수 있는 메커니즘을 마련해야 합니다
- 또한, 데이터의 순서를 보장하고, 중복 처리를 방지하기 위한 고유 식별자(ID)를 사용해야 합니다
2️⃣ 배치 처리
- 실시간으로 처리할 필요가 없는 쓰기 요청은 배치 처리를 통해 한꺼번에 처리할 수 있습니다
예를 들어, 매일 자정에 하루 동안 수집된 로그 데이터를 한 번에 DB에 저장하는 방식으로 사용할 수 있습니다.
3️⃣ 분산 DB
- 단일 DB로 모든 쓰기 요청을 처리하기 어렵다면, 분산 DB를 사용하여 부하를 분산시킬 수 있습니다
- 분산 DB는 데이터를 여러 개의 노드에 분산 저장하여 고가용성과 확장성을 제공합니다
- 또한, 샤딩(Sharding) 기법을 통해 데이터베이스를 수평으로 분할하여 각 샤드가 독립적으로 쓰기 작업을 처리하도록 할 수 있습니다. 이를 통해 단일 노드의 부하를 줄이고, 시스템의 전체 성능을 향상시킬 수 있습니다.
3. 데이터 일관성 유지
(1) 분산 트랜잭션
- 분산 트랜잭션은 여러 개의 독립된 시스템이나 데이터베이스에서 동시에 일어나는 트랜잭션을 일관되게 관리하는 방법입니다.
- 모든 시스템이 해당 트랜잭션을 성공적으로 완료하거나, 모든 시스템이 트랜잭션을 실패로 처리하도록 보장합니다. 이를 통해 데이터의 일관성을 유지할 수 있습니다.
- 분산 트랜잭션이 필요한 이유는 마이크로서비스 아키텍처에서 여러 서비스가 독립적으로 운영되기 때문입니다
1️⃣ 분산 트랜잭션 장/단점
장점
- 데이터 일관성 보장:
- 분산된 여러 데이터 소스에 걸쳐 일관된 데이터 상태를 유지할 수 있습니다. 모든 트랜잭션이 성공적으로 완료되거나 모두 실패하도록 보장합니다.
- 확장성:
- 분산 트랜잭션을 통해 여러 시스템이 독립적으로 동작하면서도, 필요한 경우 협력하여 일관된 상태를 유지할 수 있습니다. 이를 통해 시스템의 확장성을 높일 수 있습니다.
- 신뢰성:
- 트랜잭션의 ACID 속성을 분산 환경에서도 유지할 수 있어 시스템의 신뢰성을 높입니다. 데이터 무결성과 일관성을 유지할 수 있습니다.
- 복구 가능성:
- 트랜잭션 실패 시 롤백 메커니즘을 통해 상태를 복구할 수 있어, 시스템 안정성을 높일 수 있습니다.
단점
- 복잡성 증가:
- 분산 트랜잭션을 구현하고 관리하는 것은 복잡합니다. 특히, 여러 시스템 간의 트랜잭션 동기화와 데이터 일관성을 유지하는 것은 어려운 작업입니다.
- 성능 저하:
- 2PC와 같은 프로토콜을 사용할 경우, 트랜잭션의 준비와 커밋 단계에서 지연이 발생할 수 있습니다. 이는 시스템의 성능을 저하시킬 수 있습니다.
- 네트워크 오버헤드:
- 여러 시스템 간의 통신이 필요하므로 네트워크 오버헤드가 증가할 수 있습니다. 이는 트랜잭션 처리 속도를 저하시킬 수 있습니다.
- 복구의 어려움:
- 분산 트랜잭션 실패 시, 모든 시스템에서 일관된 상태로 롤백하는 것이 어려울 수 있습니다. 특히, 부분적으로 실패한 트랜잭션을 처리하는 데 어려움이 있을 수 있습니다.
(2) 이벤트 소싱
- 이벤트 소싱은 데이터 상태 변화를 이벤트로 기록하고, 해당 이벤트들을 순차적으로 재생하여 현재 상태를 파악하는 방법입니다.
1️⃣ 주요 개념
(1) 이벤트
- 데이터의 상태 변화를 나타내는 기록으로 "주문 생성","결제 완료","주문 취소"등이 해당된다.
(2) 이벤트 스토어
- 이벤트를 저장하는 공간으로 전통적인 데이터베이스 대신 이벤트를 순서대로 저장하는 스토리지이다.
(3) 에그리게이트
- 관련된 이벤트를 모아 현재 상태를 재현할 수 있는 엔티티
2️⃣ 장점
(1) 데이터 변경 이력 추적
- 모든 상태 변화를 이벤트로 기록하므로, 데이터 변경 이력을 완벽하게 추적할 수 있습니다. 이는 감사와 디버깅에 유용합니다.
(2) 복구 및 재생
- 이벤트를 재생하여 시스템의 현재 상태를 복구할 수 있습니다. 이는 데이터 손실이나 시스템 장애 시 유용합니다.
(3) CQRS와의 자연스러운 통합
- 관련된 이벤트를 모아 현재 상태를 재현할 수 있는 엔티티
3️⃣ 단점
(1) 복잡성 증가
- 시스템 설계와 구현의 복잡성이 증가합니다. 이벤트 모델링과 이벤트 스토어 관리가 필요합니다.
(2) 읽기 성능
- 이벤트를 재생하여 현재 상태를 계산해야 하므로, 읽기 성능이 저하될 수 있습니다. 이를 해결하기 위해 프로젝션과 CQRS를 활용할 수 있습니다.
(3) CQRS
- 명령(Query)과 조회(Query)의 책임을 분리하는 소프트웨어 디자인 패턴