ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 실시간 데이터 집계 시스템 아키텍처
    Architecture 2023. 7. 17. 13:15

    우리가 사용하는 많은 서비스를 통해 실시간으로 데이터가 집계되는 기능을 매일매일 접하고 있다. 유투브의 라이브 방송에서 시청자 수를 확인한다거나, 게시판 글의 개수처럼 Count를 본다거나 해야할 때다.

    그런데 현업에서 오래 일하다 보니 PO/PM Side에서는 '실시간 데이터 집계'라는 기능 자체를 굉장히 쉽게 생각하는 경우가 많아서 구현 난이도에서 발생한 Gap으로 인해 갈등이 일어나거나 PO, PM과 개발자 모두 '실시간 데이터 집계'라는 요구사항에 매몰되서 Over Engineering하는 경우가 많았다. '실시간'이라는 요구사항이 가지는 무게는 생각보다 무겁고, 난이도는 어렵다.

     

    무엇이 중요하고 무엇이 중요하지 않은지 판별할 수 있는 것이 Expert의 역할이고 역량이라고 생각한다.

    왜냐하면 실시간 데이터 집계라는 요구사항에, 1. 꼭 실시간으로 보여주어야 하는가?, 2. 현재 시스템 규모에서 감당할 수 있는 트래픽인가? 두가지 질문에 냉정하게 검토한다면 의외로 쉽고 빠르게 결정할 수 있을 것이다. 

     

    아래는 트래픽별로 실시간으로 집계한다는 가정하에 작성되었다.

     

    1. 소규모 트래픽인 경우

    소규모 트래픽일 경우에는 오히려 간단하게 해결할 수 있다. 

     

    Solution 1. DB 테이블에 집계 정보를 저장할 컬럼 또는 테이블을 만들고 집계 대상 이벤트(추가, 수정, 삭제등)가 인입됐을때 비즈니스 로직(Query)을 처리하는 트랜잭션 말미에 집계를 저장하는 Query를 실행하는 방법이다. DB 관점에서는 아래처럼 순차적으로 실행될 것이다.

    transaction begin
    
    /*Business Query Logic*/
    Insert into board_AAA Values ...;
    
    /*Agg. Query Logic*/
    Update article_count set agg_count = agg_count+1 where target='board_AAA';
    
    commit
    end transaction

    한가지 유의해야할 점은 Update or Insert Query로 집계를 내다보니 Transaction에 대한 처리를 주의해야 한다. agg 컬럼값을 Select로 가져와서 '+1'하고 Query의 변수로 값을 대입하는 방식이면 트랜잭션에서 커밋과 롤백에 의해서 집계가 달라지는 경우가 생기기 때문이다. 그래서 위의 Query 예제에서 'agg = agg+1'를 사용해서 이부분을 대응한 것이다. 

     

    조금 더 트래픽이 늘어나거나 확장성을 고려한다면 집계 데이터를 DB 컬럼/테이블이 아닌 Cache 서비스를 사용할 수도 있다. 대표적인 솔루션은 Redis이고 트랜잭션의 성패에 따라 데이터를 업데이트하는 로직으로 한다.

     

    2. 중간규모 트래픽인 경우

    중간규모 트래픽이라는 표현이 모호해서 'Request가 초당 몇개인가?'라고 구체적으로 궁금할 수 있다. 그런데 1M/sec라고 딱 잘라서 이야기할 수 없는 것이 내부 시스템과 비즈니스 로직에 따라서 확장을 해야하는 시기는 천차만별이기 때문이다. 대신, 중간규모에 대응이 필요한 시기라고 판단하는 것은 1. Request별 Latency의 증가, 2. 서버 Load의 증가, 3. 관련 장애 발생이다. 즉, 트래픽이 상승하면서 기존의 Capability를 넘어서면서 문제가 생기는 것이 Signal이다.

     

    Solution 2. 이때부터는 Event-driven Architecture를 도입하는 것을 적극적으로 검토해야 한다. 트랜잭션을 분리하고 Kafka같은 MQ 시스템을 도입해야한다. 아래의 그림처럼 시스템이 확장될 것인데 Solution 1 확장판에서 크게 다르지 않고 MQ가 추가되어 트랜잭션이 명확하게 분리되는 것이 큰 변화라고 하겠다. 이 부분이 가장 중요한 변곡점이라고 생각하는 것은 서비스가 대용량 트래픽으로 성장했을때 대응이 훨씬 용이해지기 때문이다. 트래픽이라는게 1M에서 10M로 늘어난 10배 성장은 이렇게 저렇게 커버할 수 있어도 10M → 100M, 100M → 1G는 완전히 다른 차원의 문제이기 때문이다. 이 아키텍처를 기반으로 각 요소들을 Scale Up/Out을 통해 왠만한 대용량 트래픽을 처리할 수 있기 때문이다.

     

    대규모 트래픽으로 넘어가기 전에 Kafka Topic의 Partition을 나누고 각 Partition별로 Consumer를 분리하는 방법을 통해 5x에 대응하는 과정을 거치는 것이 좋다.

     

    3. 대규모 트래픽인 경우

    대규모 트래픽은 Solution 2에서 언급했듯이 각 요소들을 확장하는 형태로 많은 부분이 커버될 것이다.

    가장 대표적인 확장 포인트는 "궁극적으로 전체 Aggregation을 커버하기 위한 전용 마이크로(?) 서비스 시스템 구축"이다.

    이와같이 대규모 트래픽인 상황에서는 시스템을 확장함에 있어서 튜닝과 개선이 아닌 근본적으로 다른 접근법을 사용해야 한다. 이쯤되면 비즈니스 DB도 RDB가 아닌 NoSQL, BigData 부류로 넘어가는 것을 고민해야 하고 집계를 비롯한 몇몇 Feature들은 별도 서비스 시스템으로 분리해야 한다. 중간규모에 비해 난이도가 엄청나게 올라가지만 이런 시스템 확장을 경험한다면 엔지니어에겐 어디서 경험하기 힘든 축복스런 상황이다.

     

    Conclusion

    위에서 제시한 아키텍처는 정답이 아닙니다. 하지만 제일 많이 일반적으로 접근하는 방법입니다.

    실시간 데이터 집계라는 아키텍처 주제에서 '이렇게 하세요!'라는 Silver Bullet을 제시하는 것보다 시스템 디자인을 하기 전에 현재 시스템 트래픽이 어느 정도냐에 따라 다르게 접근해야하고 설계해야 한다는 것을 개념적으로 이야기하고 싶었습니다.

    참고 차원에서 가볍게(?) 읽어보시면 좋겠습니다. 😄

     

    Thanks

    Hans

Designed by Tistory.