-
Notifications
You must be signed in to change notification settings - Fork 0
Description
도입 배경
우리는 처음에 "DB의 데이터는 언제나 실시간으로 정확해야 한다"는 대전제를 가지고 있었다.
하지만 대규모의 트래픽이 몰리는 선착순 주문 이벤트 환경에서 이 전제는 시스템의 가용성과 성능을 심각하게 저해하는 병목이 되었다.
1. 배경 및 문제 정의
우리는 RDBMS의 물리적 한계(Lock 대기, 커넥션 고갈)를 극복하기 위해 Redis Lua Script를 도입하여 재고 관리를 Redis로 옮겼다.
이로 인해 발생하는 Redis와 DB 간의 데이터 불일치를 해결해야 하며, 이를 위해 Eventual Consistency(최종적 일관성) 모델을 적용하고자 했다. 이때 다음 3가지 포인트를 두고 구현 방법에 대해 고민했다.
효율성: 불필요한 DB I/O를 얼마나 줄일 수 있는가안전성: 장애 발생 시 데이터 유실 위험은 없는가복구: 데이터가 틀어졌을 때 얼마나 빠르고 정확하게 복구할 수 있는가
2. 구현 전략별 상세 분석 및 비교
(1) Spring Event 방식 : Sold Out 시점에만 동기화
Redis 재고가 0이 되는 순간(Sold Out), 이벤트를 발생시켜 DB를 0으로 업데이트하는 방식이다.
- 효율성
- 재고가 100개든 100만 개든, DB 업데이트 쿼리는 단 1회만 발생함. 리소스 낭비가 없음
- 안전성
- Save Point 가 없음
- 만약 재고가 1개 남은 시점에 서버가 다운되면, 그동안 판매된 99개의 재고 감소 내역이 DB에 전혀 반영되지 않은 상태로 증발함
- 복구
- Redis 데이터가 날아가면 DB는 여전히 초기값을 가리키고 있음
- 복구를 위해서는 orders 테이블의 전체 주문을 count 하여 계산해야 하는데, 대규모 트래픽 상황에서 이는 DB에 치명적인 부하를 줄 수 있음
(2) Spring Scheduler : 주기적으로 동기화
짧은 주기(예: 10초, 1분)로 Redis의 현재 값을 DB에 덮어쓰는 방식이다.
- 효율성
- 정해진 시간마다 동기화 쿼리가 발생함(ex. 1분에 1번 실행 = 분당 쿼리 1회).
- RDBMS 입장에서 부하라고 부를 수 없는 수준
- 안전성
- Save Point : 주기적으로 DB에 값을 저장하므로, 서버가 터져도 최대 '마지막 주기 이후'의 데이터만 오차가 발생
- 복구
- Redis가 죽어도 DB에는 "1분 전 데이터"가 남아있음.
- “Spring Event 방식” 과 마찬가지로 주문 상품에 대한 복구용 쿼리가 필요함
- 다만, 장애 복구 시 전체 주문 상품 테이블을 Full Scan 하지 않고,
마지막 동기화 시점 이후의 데이터만 조회(Range Scan)하면 되므로 복구 속도가 획기적으로 빠를 것으로 생각함
(3) Message Queue (Kafka/RabbitMQ)
Redis에서 발생한 재고 변경 이벤트를 외부 메시지 브로커(Kafka 등)에 발행하고, 별도의 워커(Consumer)가 이를 비동기로 받아 DB에 반영하는 방식이다.
- 효율성
- 트래픽이 폭주해도 DB가 처리할 수 있는 속도에 맞춰 컨슈머가 메시지를 천천히 가져가므로, DB 부하를 일정하게 유지 할 수 있음
- 즉 컨슈머에서 메시지를 1건씩 처리하지 않고, 100건~1,000건씩 묶어서
Bulk Insert/Update처리하여 DB 네트워크 비용 절감 가능
- 안전성
- 메시지가 브로커의 디스크에 저장되므로, 컨슈머 서버나 DB 서버가 셧다운되어도 데이터가 유실되지 않음 → 메모리 큐 방식의 단점 해결
- At-least-once 처리를 보장하여, 일시적인 네트워크 오류가 발생해도 재시도를 통해 데이터 정합성을 맞춤
- 복구
- DB 점검이나 장애로 인해 장시간 다운되더라도, 메시지는 큐에 안전하게 쌓여있음
→ DB 복구 후 컨슈머를 다시 켜면 순차적으로 처리되어 데이터가 자동 복구 됨 - DB 장애가 전체 주문 서비스의 장애(Redis 차감 실패)로 전파되지 않음.
- DB 점검이나 장애로 인해 장시간 다운되더라도, 메시지는 큐에 안전하게 쌓여있음
- 단점 및 한계
Redis 재고 차감은 성공했는데, 네트워크 이슈로 메시지 발행에 실패한다면?이라는 문제를 해결하기 위한 고민이 추가적으로 필요함- 높은 운영 비용 : 카프카 클러스터 구축, 모니터링, 파티션 관리 등 인프라 복잡도가 급격히 상승함. 초기 모델에는 과도한 스펙일 수 있음.
3. 결정
현재 이 프로젝트의 존재 의의는 '구현의 편의성'보다는
대규모 트래픽 환경에서의 확실한 문제 해결이 목표라고 생각했다.따라서 Scheduler나 Event와 같은 과도기적 방법 대신, 초기 설계 단계부터 데이터의
안전성과복구 능력을 보장할 수 있는 Message Queue(MQ) 기반의 아키텍처를 도입하는 것이 맞다고 판단했다.
기술 스택 선정 (todo)
MQ 도입이라는 방향성은 확정되었으나, 현재 이 프로젝트의 특성(선착순 이벤트의 순간 트래픽 규모, 데이터 복구의 중요성, 운영 복잡도 등)에 가장 적합한 솔루션이 무엇인지는 생각이 필요하다.
따라서 차기 단계로 Kakfa vs. rabbitMQ 등의 성능, 안정성, 운영 비용을 비교 분석해서 기술 스택을 선정할 생각이다.