대규모 트랜잭션을 처리하는 배민 주문시스템 규모에 따른 진화 #우아콘2023 #우아한형제들
- 배민의 주문시스템 개발팀 : 장바구니 → 주문하기 → 주문내역
- 배민 주문 시스템의 특징 :
- 12시, 18:30 점심/저녁식사 특정 시간대에 트래픽이 몰림
- 커머스 + 푸드 도메인
- MSA 구조 : 가게, 메뉴, 주문, 결제, 배달 등의 수많은 시스템이 통신하고 있음
- 대용량 데이터 : 일 평균 300만건의 주문
- 대규모 트랜잭션 : 특히 식사시간대 순간적으로 더 많은 트랜잭션
- 여러 시스템과 연계 : 이벤트 기반으로 다른 시스템과 통신
- 매년 주문수가 가파르게 증가
- 여러 성장통들
- 1. 단일 장애 포인트
- 증앙 집중 DB의 장애 → 전체 시스템으로 전파
- ASIS 아키텍처 : 주문 시스템 → 루비 (단일 포인트) → 주문 중계, 가게, .. 등
- 탈 루비 프로젝트 : 각 시스템에 맞는 도메인을 모델링 후 디비 분리 → MQ를 이용한 이벤트 기반 통신 & 시스템 간 영향도 분리
- 2. 대용량 데이터
- 대용량 데이터 → DBMS 조인으로 조회 성능 하락
- 주문 요청 이벤트 → 주문 API → 주문 DB
-
→ 주문 MQ → 주문 이벤트 처리 & 외부 시스템
- 주문 조회 쿼리 → 주문 인터널 API → 주문 DB
- 주문 RDBMS에서 주문 저장과 조회가 함께 일어나고 이씀
- 주문내역을 보여주기 위해서는 아주 결제,주문,메뉴,배달 등 여러 테이블을 함께 조회해야 함 → 조회성능 문제
- → 역정규화 → MongoDB NoSQL 단일 Document
- → 어떻게 동기화??
- 주문 도메인 생명 주기 : 주문 생성 → 주문 접수 → 배달완료 / 주문취소 → 이 사이에서만 데이터 변경이 일어남.
- → CQRS 패턴 적용
- → 주문 API에서는 이전처럼 정규화된 RDMBS에 주문 내역 저장 & 이벤트를 수신한 주문한 주문 이벤트 처리기에서 몽고DB에 저장
- 커멘드 모델과 조회 모델을 분리 & 조회 모델 역정규화
- 3. 대규모 트랜잭션
- 주문 DB의 분당 쓰기 처리량의 한계치에 도달함.
- 주문 DB 구성
- 주문 API → 주문RW Primary → 스펙 업으로만 대응 가능
- 주문 인터널 API → 주문 DB RO Replica → ScaleOut으로 대응 가능
- → 샤드 클러스터 구성 : RW DB 를 스케일 아웃하자!
- → 그러나 AWS 오로라는 샤딩을 지원하지 않음
- → 어플리케이션 샤딩을 지원하자!
-
- 어떤 샤드에 저장할지 고민하는 샤딩 전략에 대한 고민
- 여러 샤드에 저장된 데이터를 애그리게이트하는 방법에 대한 고민
-
- 어떤 샤딩전략을 사용할까?
-
- Key based Sharding : Shard Key를 이용하여 데이터 소스를 결정
- 구현이 간단하고 데이터를 골고루 분배할 수 있으나 장비를 추가/제거하기 어려움 (재배치 필요)
- Range based Sharding : 값의 범위 기반으로 데이터 분산
- Range로 구현하기 때문에 구현이 간단하지만, 특정 샤드에 데이터가 몰리는 핫스팟 발생 가능성
- Directory based Sharding : 샤드가 어떤 데이터를 가질지 룩업 테이블 구성
- Key Based와 비슷하지만 인덱스 같은 룩업 테이블을 가지고 있음.
- 동적으로 샤드 추가하는데 유리하지만 단일 장애포인트가 될 수 있는 문제점
-
- 기존 주문 시스템의 특징은?
- 주문이 정상 동작하지 않으면 배민 전체의 장애라고 인지하는 니쁜 경험을 줄 수 있음 → 단일 장애 포인트는 피하자
- 동적 주문데이터는 최대 30일만 저장한다 (배달 특성상 하루안에 생명주기가 끗) → 샤드 추가 이후 30일이 지나면 데이터는 다시 균등하게 분배될 것
- → 주문번호를 샤드키로 사용해 Key Based Sharding 전략 사용
- 어플리케이션 구현은 어떻게?
- AOP, AbstractRoutingDataSource 이용해서 구현
- 4. 다건 조회시 애그리게이트
- 다건 조회는 몽고DB를 활용
- 샤딩으로 인해 증가하는 트랜잭션을 스케일아웃으로 대응 가능해짐
- 4. 복잡한 이벤트 아키텍처
- 규칙성 없는 무분별한 이벤트 발생
- 주문 도메인 로직 (주문 라이프 사이클 내) & 기타 서비스로직 (알림 발송, 영수증 등)으로 분리
- 주문 시스템 이벤트 아키텍쳐 : 주문 도메인 로직 → Spring event로 발행 → OrderOpenPostProcessService, OrderAcceptPostPorcessService등이 구독해서 로직 수행
- 시스템 관점으로 바라본 아키텍쳐 : 스프링 애플리케이션 이벤트는 로직을 수행하는 주체를 파악하기 어려워짐