ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Tech] 우아콘 WOOWACON 2023 - 대규모 트랜잭션을 처리하는 배민 주문시스템 규모에 따른 진화 세션 학습
    Tech 2024. 1. 8. 09:18
    반응형

     

    우아콘 2023 컨퍼런스에 신청을 했지만... 당첨 되지 못한 관계로 😢

    유튜브에 업로드 된 우아콘 2023 ‘대규모 트랜잭션을 처리하는 배민 주문시스템 규모에 따른 진화’ 세션을 시청한 후, 내용을 기록하고 공유하고자 합니다.

     

     

    출처: https://www.youtube.com/watch?v=704qQs6KoUk

     

     

    목차

    대규모 트랜잭션을 처리하기 위한 주문 시스템의 진화 소개

    • 단일 장애 포인트 (하나의 시스템 장애는 전체 시스템의 장애로 이어짐)
    • 대용량 데이터 (조인 연산으로 인한 조회 성능 저하)
    • 대규모 트랜잭션 (주문수 증가로 저장소의 쓰기 처리량 한계에 도달)
      • 🚀 샤딩(Sharding):
      • 3가지 샤딩 전략의 장,단점
      • Key Based Sharding 전략 사용
      • 샤드 번호  추출 
      • 데이터 소스 결정
      • 다건 조회 애그리게이트
      • 샤딩 적용 아키텍처
    • 복잡한 이벤트 아키텍처 (규칙 없는 이벤트 발행으로 서비스 복잡도 증가)
      • 기존 구조의 문제점
      • 내부 / 외부 이벤트 정리
      • Zero Payload 전략 이용
      • 이벤트 발행 실패 유형
      • 요약

     

     

    대규모 트랜잭션을 처리하기 위한 주문 시스템의 진화 소개

     

    출처: https://www.youtube.com/watch?v=704qQs6KoUk

     


    단일 장애 포인트 (하나의 시스템 장애는 전체 시스템의 장애로 이어짐)

    문제점
    - 중앙 집중 저장소 방식이여서 하나의 시스템에서 장애가 발생하면 전체 시스템의 장애로 이어진다.

     

    해결방안
    - 도메인별로 저장소를 분리하고, 시스템 간 통신은 Message Queue 기반으로 통신한다.
    - 시스템 장애 후, 복구 되면 MQ의 재소비 특성으로 이벤트가 재발행 된다.
    - 즉, MQ를 이용한 이벤트 기반 통신으로 시스템 간 영향도를 분리한다.

     

    대용량 데이터 (조인 연산으로 인한 조회 성능 저하)

    출처: https://www.youtube.com/watch?v=704qQs6KoUk

     

    ⬇️

     

    출처: https://www.youtube.com/watch?v=704qQs6KoUk

     

    문제점
    - 루트 애거리거트를 기반으로 여러 조인 연산이 발생하여 성능 저하 발생

     

    해결방안
    - 역정규화를 통해 Mongo DB를 사용한 단일 도큐먼트를 사용
    - 주문 API → 주문 이벤트 발행 → ‘주문 이벤트 처리기’가 이벤트 수신시 Mongo DB에 데이터 동기화
    - 주문 API (쓰기 전용)을 통해 RDB에 데이터를 저장함으로써 정합성을 보장하고, 주문 인터널 API (읽기 전용) 을 통해 Mongo DB를 조회하여 성능을 높임 - CQRS(Command and Query Responsibility Segregation) 아키텍처
    - 즉, 커맨드 모델과 조회 모델을 분리하고 조회 모델 역 정규화를 통해 조회 성능 개선


    대규모 트랜잭션 (주문수 증가로 저장소의 쓰기 처리량 한계에 도달)

    문제점
    - Primary 1대로 쓰기 처리를 하고, 스케일아웃을 통한 여러대의 replica를 통해 읽기 부하 분산 처리중이였음
    - Reader 인스턴스는 스케일 아웃으로 대응이 가능했지만, Writer 인스턴스는 스펙을 올리는 방법 밖에 없었고,
    분당 쓰기 처리량을 감당할 수 없게 됨.
    - 샤드 클러스터를 구성하여 Writer도 스케일아웃을 통해 분산처리를 하고자 했으나 Aws Aurora는 샤딩을 지원하지 않았음

     

    해결방안
    - 애플리케이션 내에서 샤딩 전략을 사용한다.
    - 클러스터 내 어느 샤드에 접근할지 결정하는 샤딩 전략 고민이 필요
    - 여러 샤드에 있는 데이터를 시간순으로 애그리게이트 하는 방법에 대한 고민 필요

     

    3가지 샤딩 전략의 장,단점

    1. Key Based Sharding (a.k.a Hash Based Sharding)

    출처: https://www.youtube.com/watch?v=704qQs6KoUk

     

    • Shard Key를 이용하여 데이터소스를 결정하는 방식
    • 장점 : 구현이 간단하며, 샤드 클러스터 내 샤드들에 데이터를 골고루 분배할 수 있다.
    • 단점 : 장비를 동적으로 추가, 제거할 때 데이터 재배치가 필요하다.

     

    2. Range Based Sharding

     

    출처: https://www.youtube.com/watch?v=704qQs6KoUk

     

    • 값의 범위 (Range) 기반으로 데이터를 분산시키는 방식
    • 장점 : 특정 값의 범위 기반으로 샤드를 결정하면 되기 때문에 구현이 간단하다.
    • 단점 : 데이터가 균등하게 배분되지 않아 특정 샤드에 데이터가 몰리면 Hotspot이 되어 성능 저하가 발생할 수 있다.

     

    3. Directory Based Sharding

    출처: https://www.youtube.com/watch?v=704qQs6KoUk

     

     

    • 샤드가 어떤 데이터를 가질지 look up table을 유지하는 방식
    • 장점 : 샤드 결정 로직이 Look Up Table로 분리되어 있기 때문에 동적으로 샤드를 추가하는데 유리하다.
    • 단점 : Look Up Table이 단일 장애 포인트가 될 수 있다.



    Key Based Sharding
    전략을 사용하기로 결정

    주문 시스템의 동적 주문 데이터는 최대 30일만 저장하고, 보통 하루 내 주문이 이루어지기 때문에 샤드의 추가는
    많이 일어나지 않을 것이며, 추가 된다 하더라도 30일이 지나면 데이터는 다시 균등하게 분배될 것이라는 결론을 내림.
    이러한 시스템의 특징을 감안해서 Key Based Sharding (a.k.a Hash Based Sharding) 전략을 사용하기로 결정

     


    샤드 번호 추출

     

    주문번호를 통해 주문 순번을 추출할 수 있는 로직이 존재하고, 이를 통해 주문 순번을 조회한 후, 샤드수로 나눠서
    샤드번호를 결정할 수 있다.


     

    데이터 소스 결정

     


    위 과정을 통해서,
    주문번호 샤드 키를 이용한 해싱주문 순번 순으로 샤드에 고르게 분배 된다.


    다건 조회 애그리게이트

    N 개의 샤드에 분산 저장된 데이터를 어떻게 조합하여 내려줄지 고민이 필요했지만,
    이미 쓰기조회 로직Mongo DB로 분리해둔 덕분에 애플리케이션 샤딩 적용시 큰 도움이 되었다.


    샤딩 적용 아키텍처

     

    샤딩이 적용된 후, 주문 시스템이 위 사진과 같은 아키텍처가 되었고,
    샤딩으로 인해 증가하는 트랜잭션스케일 아웃으로 대응이 가능해졌다.

     

    궁극적으로, 주문 DB의 분당 쓰기 처리량 한계치 도달 문제를
    주문 DB 샤딩 분산 쓰기로 인한 처리량 분산을 통해 대규모 트랜잭션을 해결함


     복잡한 이벤트 아키텍처 (규칙 없는 이벤트 발행으로 서비스 복잡도 증가)

     

    기존 구조의 문제점

     

    OrderDomain Service는 도메인 로직을 처리하고, 해당 라이프사이클에 맞는 도메인 로직 이벤트를
    어플리케이션 이벤트인 Spring Event를 통해 발행하여, 각 서비스에서 이벤트를 수신하여 서비스 로직을 처리하던 구조

     

    문제점

    • 스프링 어플리케이션 이벤트는 로직을 수행하는 주체를 파악하기가 어려웠다.
    • 이벤트 유실이 발생할 경우 재처리가 어려웠다.

    내부 / 외부 이벤트 정리

     

    주체를 파악하기 쉽게 도메인 로직내부 이벤트로 정의하고, 서비스 로직외부 이벤트로 정의하여 분리 했다.


    Zero Payload 전략 이용

     

     

    내부 이벤트에는 데이터가 없다보니, 서비스 로직을 수행하기 위해선 한계가 있게끔 만들고,
    단순히 도메인 로직을 수행했으며, 어떤 결과가 만들어 졌는지 정도만 알려 주기 위해 Zero Payload 전략을 이용했다.

     

    외부 이벤트에는 서비스 로직만 처리 하고 주체를 파악하기 쉽게 분리한 뒤, 필요한 데이터를 그때 그때 조회하면서 조합하는 방향으로 설계를 했다. 또한 조회 성능을 극대화한 Mongo DB NoSQL 서버가 있기 때문에 큰 문제가 없다고 판단했다.

     

    즉, 서비스 로직을 처리하는 주체를 이벤트 처리기 몰아주면서 이벤트 처리 주체를 단일화 할 수 있었다.

     

    물론, 어플리케이션 이벤트 대신 SQS를 사용하면서 네트워크 비용이 발생한다는 이슈가 있었지만,
    그보다는 이벤트 처리 주체를 단일화 함으로써 서비스로직을 한 군데에서 관리하고자 하는 니즈가 더 중요했다.


    이벤트 발행 실패 유형

     

    문제점
    이벤트 발행이 실패 하는 경우로는 도메인 로직 전체가 실패하는 경우, 도메인 로직은 성공했으나 이벤트 발행 실패서비스 로직을 미수행 하는 경우로 크게 두 가지가 존재했다.

     

    첫 번째 경우는 이슈가 존재하지 않지만,

    두 번째 경우엔 재발행을 할 방법이 없어서 도메인 로직과 서비스로직의 일관성을 보장할 수가 없어졌다.

     

     

    해결방안

     

     

     

    트랜잭션이 완료되면 아웃박스 Entity에 이벤트 페이로드를 저장하는 아웃박스 패턴을 이용했다.
    이렇게 함으로써, 발행 실패 시 아웃박스 Entity에 저장된 페이로드를 배치를 통해서 재발행 할 수가 있다.

     

    요약

     

    단일 장애 포인트:

     - MSA를 적용하면서 MQ를 통한 시스템 간 이벤트 기반 통신을 통해 느슨한 결합을 가져감으로써, 서비스 간 영향을 덜어내며 해결 

     

    대규모 데이터의 조회 성능 저하: 

    CQRS패턴을 적용하면서 커맨드성, 조회성 모델을 분리하면서 조회 성능 향상 

     

    대규모 트랜잭션: 

    어플리케이션 샤딩을 적용하면서 쓰기 처리 요청 또한 분산 저장을 통해 성능 저하를 해결함 

     

    이벤트 구조 개선: 

    - 서비스 로직을 하나의 어플리케이션에 위임하고, 유실에 대해서는 트랜잭션 아웃박스 패턴을 적용하면서 해소

     

     

    여기까지가 [우아콘 WOOWACON 2023 - 대규모 트랜잭션을 처리하는 배민 주문시스템 규모에 따른 진화]의 세션 내용 입니다.

    영상을 보면서, "아는 내용이네", "이거를 저렇게 했으려나" 라는 등의 생각들이 들었습니다. 또한 생각 하지도 못했던 방식이 쓰인 것을 보고 "괜히 배민이 아니구나" 라는 생각도 들었습니다. 이번 세션 영상을 보면서 주로 느낀점으로는, 대규모 시스템의 트랜잭션을 처리하기 위해서는 정말 많은 고민이 필요하고 확장성도 중요하겠다는 생각을 했습니다. 그리고 이전에는 세션을 보더라도 잘 이해가 되지 않던 부분들이 있었는데, 이번에는 전부 이해를 하는 저를 보면서 "잘 성장 했구나" 라는 생각을 조금..... 해봤습니다! 감사합니다 😊

    반응형

    댓글

Designed by Tistory.